Definition
Make instances of classes on the fly to improve performance efficiently, like individual characters or icons on the screen.Intent
01- Use sharing to support large numbers of fine-grained objects efficiently.
- The Motif GUI strategy of replacing heavy-weight widgets with light-weight gadgets.
Problem
02Designing objects down to the lowest levels of system “granularity” provides optimal flexibility, but can be unacceptably expensive in terms of performance and memory usage.
Motivation
Some programs require a large number of objects that have some shared state among them. Consider for example a game of war, were there is a large number of soldier objects; a soldier object maintain the graphical representation of a soldier, soldier behavior such as motion, and firing weapons, in addition soldier’s health and location on the war terrain. Creating a large number of soldier objects is a necessity however it would incur a huge memory cost. Note that although the representation and behavior of a soldier is the same their health and location can vary greatly.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.
Structure
05Flyweights are stored in a Factory’s repository. The client restrains herself from creating Flyweights directly, and requests them from the Factory. Each Flyweight cannot stand on its own. Any attributes that would make sharing impossible must be supplied by the client whenever a request is made of the Flyweight. If the context lends itself to “economy of scale” (i.e. the client can easily compute or look-up the necessary attributes), then the Flyweight pattern offers appropriate leverage.
Examples
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. C:\ Command Prompt
C:\> java Test |
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.
C:\ Command Prompt
C:\> java Test
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 |
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:\ Command Prompt
C:\> java -Dcom.sun.management.jmxremote Test |
C:\ Command Prompt
C:\> jconsole |
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
No comments:
Post a Comment