 |
| |
Structural Pattern
Flyweight Pattern
|
|
Flyweight Pattern
|
Define
flyweight pattern
|
Make instances of classes on the
fly to improve performance efficiently, like individual characters or
icons on the screen.
|
Where
to use & benefits
|
Need to instantiate a large
amount of small and fine-grained classes.
Need icons to represent object.
An object extrinsic state can be shared by classes.
Reduce the number of objects created, decrease memory footprint and
increase performance.
Increase runtime cost associated with transferring, finding, or
computing extrinsic data.
Related patterns include
- Composite, which supports recursive structures, whereas an
flyweight is often applied on it.
- Factory Method, which produces specific object upon
requirement, whereas an flyweight uses it to reduce objects.
- State, which allows an object to alter its behavior when
its internal state is changed, whereas a flyweight is best implemented
on it.
- Strategy, which allows an algorithm vary independently to
suit its needs, whereas a flyweight is based on such strategy.
|
|
Example
of flyweight pattern
|
In order to share an object, we
may declare an interface and an intrinsic state through which
flyweights can receive and act on it. If you want to show a file system
with folders to show the directories or subdirectories, you don't need
to load all the files or directories at one loading time. You may show
the upper level folders first. If the user clicks a folder, then load
its subdirectories and files. The shared trigger is mouse-clicked. The
composite pattern may be combined to define the flyweight system.
class Folder {
void draw(..) {}
}
class FolderFactory {
...
if (selected) {
return aFolder;
else
return aFile;
...
}
...
To show how to use flyweight to reduce object creation, we will make a
program to draw 1000 circles with 6 different colors. Before we
customize it to a flyweight design, it is coded as follows:
import java.awt.*;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class Test extends JFrame{
private static final Color colors[] = { Color.red,
Color.blue,
Color.yellow, Color.orange,
Color.black, Color.white };
private static final int WIDTH_ = 400,
HEIGHT = 400,
NUMBER_OF_CIRCLES = 1000;
public Test() {
Container contentPane = getContentPane();
JButton button = new JButton("Draw
Circle");
final JPanel panel = new
JPanel();
contentPane.add(panel,
BorderLayout.CENTER);
contentPane.add(button,
BorderLayout.SOUTH);
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
button.addActionListener(new
ActionListener() {
public void
actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();
for(int i=0; i < NUMBER_OF_CIRCLES; ++i) {
g.setColor(getRandomColor());
int r = getRandomR();
g.drawOval(getRandomX(), getRandomY(), r, r);
}
}
});
}
private int getRandomX() {
return (int)(Math.random()*WIDTH );
}
private int getRandomY() {
return (int)(Math.random()*HEIGHT);
}
private int getRandomR() {
return (int)(Math.random()*(HEIGHT/10));
}
private Color getRandomColor() {
return
colors[(int)(Math.random()*colors.length)];
}
public static void main(String[] args) {
Test test = new Test();
}
}
Copy, paste above code, and run it to see the functionality.
Output :

Note that the above program doesn't take advantage of reusability of
OOP. We will customize it and make a Circle object, so the Circle
object can be reused.
class Circle {
private Color color;
public Circle(Color color) {
this.color = color;
}
public void draw(Graphics g, int x, int y, int r) {
g.setColor(color);
g.drawOval(x, y, r, r);
}
}
Then we rewrite the program. It is possible for people to rewrite with
Circle object in the following way:
import java.awt.*;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class Test extends JFrame{
private static final Color colors[] = { Color.red,
Color.blue,
Color.yellow, Color.orange,
Color.black, Color.white };
private static final int WIDTH = 400,
HEIGHT = 400,
NUMBER_OF_CIRCLES = 1000;
public Test() {
Container contentPane = getContentPane();
JButton button = new JButton("Draw
Circle");
final JPanel panel = new
JPanel();
contentPane.add(panel,
BorderLayout.CENTER);
contentPane.add(button,
BorderLayout.SOUTH);
setSize(WIDTH ,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
button.addActionListener(new
ActionListener() {
public void
actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();
for(int i=0; i < NUMBER_OF_CIRCLES; ++i) {
Circle circle = new Circle(getRandomColor());
circle.draw(g, getRandomX(), getRandomY(), getRandomR());//1000 object
created.
}
}
});
}
private int getRandomX() {
return (int)(Math.random()*WIDTH );
}
private int getRandomY() {
return (int)(Math.random()*HEIGHT);
}
private int getRandomR() {
return (int)(Math.random()*(HEIGHT/10));
}
private Color getRandomColor() {
return
colors[(int)(Math.random()*colors.length)];
}
public static void main(String[] args) {
Test test = new Test();
}
}
From the above code, you may note that 1000 circle object has been
created. It is memory consuming.
To improve it, we will create a CircleFactory class to customize it by
using flyweight design pattern. Since we just draw circle with
different colors, we can store color info in a hashmap. If a circle has
been drawn, the new circle will be checked with color. If the circle
with the same color has been found in the hashmap, the circle will
share the instance which is picked up from the hashmap instead of
creating a new one. We will reuse the object with different state, that
is to say we will share the instance and draw the circle with different
start position and radius on the fly.
class CircleFactory {
//store color
private static final HashMap circleByColor = new HashMap();
public static Circle getCircle(Color color) {
Circle circle =
(Circle)circleByColor.get(color);
if(circle == null) {
circle = new
Circle(color);
circleByColor.put(color, circle);
System.out.println("Creating " + color + " circle");//see how many
objects we create on command line
}
return circle;
}
}
So our test program will be coded as follows:
import java.awt.*;
import java.util.HashMap;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class Test extends JFrame{
private static final Color colors[] = { Color.red,
Color.blue,
Color.yellow, Color.orange,
Color.black, Color.white };
private static final int WIDTH = 400,
HEIGHT = 400,
NUMBER_OF_CIRCLES = 1000;
public Test() {
Container contentPane = getContentPane();
JButton button = new JButton("Draw
Circle");
final JPanel panel = new
JPanel();
contentPane.add(panel,
BorderLayout.CENTER);
contentPane.add(button,
BorderLayout.SOUTH);
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
button.addActionListener(new
ActionListener() {
public void
actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();
for(int i=0; i < NUMBER_OF_CIRCLES; ++i) {
Circle circle = CircleFactory.getCircle(getRandomColor());
circle.draw(g, getRandomX(), getRandomY(),getRandomR());
//Since we have 6 different colors, we have 6 objects created.
}
}
});
}
public static void main(String[] args) {
Test test = new Test();
}
private int getRandomX() {
return (int)(Math.random()*WIDTH );
}
private int getRandomY() {
return (int)(Math.random()*HEIGHT);
}
private int getRandomR() {
return (int)(Math.random()*(HEIGHT/10));
}
private Color getRandomColor() {
return
colors[(int)(Math.random()*colors.length)];
}
}
class CircleFactory {
private static final HashMap circleByColor = new HashMap();
public static Circle getCircle(Color color) {
Circle circle =
(Circle)circleByColor.get(color);
if(circle == null) {
circle = new
Circle(color);
circleByColor.put(color, circle);
System.out.println("Creating " + color + " circle");
}
return circle;
}
}
class Circle {
private Color color;
public Circle(Color color) {
this.color = color;
}
public void draw(Graphics g, int x, int y, int r) {
g.setColor(color);
g.drawOval(x, y, r, r);
}
}
Copy, paste above code and run it. You will see the printout from the
command line, that you only have 6 objects created, not 1000 objects
because you only have 6 colors. Such a big reduction of object creation
will improve your program performance dramatically.
Output :
Creating java.awt.Color[r=255,g=0,b=0] circle
Creating java.awt.Color[r=0,g=0,b=0] circle
Creating java.awt.Color[r=255,g=200,b=0] circle
Creating java.awt.Color[r=255,g=255,b=0] circle
Creating java.awt.Color[r=0,g=0,b=255] circle
Creating java.awt.Color[r=255,g=255,b=255] circle

Flyweight design is effective with instantiating a large amount of
small and fine-grained classes by combining with factory design
pattern.
If you have jdk1.5 installed, you may need to use a tool to check if
you save the memory by running your commands as follows:
C:\\> java -Dcom.sun.management.jmxremote Test
And open another console as follows:
C:\\> jconsole
The jconsole tool will hook up your program Test to give your
statistic numbers about your program, such as threads, heap, memory,
VM, etc.
String class is designed with Flyweight design pattern. It has similar
structure as above example. When you create a string constant, such
constant is stored in a pool. When the second string is created, it
will be checked to see if it has been created. If it is true, the
second string instance will be picked up from the string pool instead
of creating a new one. This is why the following code makes sense, but
bothers many people.
String s1 = "hello";
String s2 = "hello"; //store in a string pool.
String s3 = new String("hello");
System.out.println(s1==s2); //true, share the same memmory address
System.out.println(s1==s3); //false
|
|
|

|
|