您的位置:首页 > 其它

设计模式11 - Flyweight

2012-12-06 19:49 218 查看
Flyweight is used when there is a need to create high number of objects of almost similar nature. High number of objects consumes high memory and flyweight design pattern gives a solution to reduce the load on memory by sharing objects. It is achieved by segregating
object properties into two types intrinsic and extrinsic. In this article lets see about this in detail with a real world example and respective java implementation.

Intent as stated by GoF is, “Use sharing to support large numbers of fine-grained objects efficiently”. Sharing is key in flyweight pattern and we need to judiciously decide if this pattern can be applied.


When to Use Flyweight Design Pattern

We need to consider following factors when choosing flyweight,

Need to create large number of objects.
Because of the large number when memory cost is a constraint.
When most of the object attributes can be made external and shared.
The application must not mandate unique objects, as after implementation same object will be used repeatedly.
Its better when extrinsic state can be computed rather than stored. (explained below)

Flyweight is all about memory and sharing. Nowadays an average desktop comes with 500 GB hard disk, 4GB ram and with this you can stuff your whole home inside and will still have remaining space to put an elephant in it. Do we really need to bother about memory
and usage? Since the cost has come down there is no restriction to use it effectively. Think about mobile devices that are increasing everyday and they still have memory constraint.



Even if you have huge memory, in some cases the application may need efficient use of it. For example assume we are working with an application that maps stars from universe. In this application if we are going to create an object for every star then think
of it how much memory we will need. Gang of Four (GoF) have given
an example of text editors in their book. If we create an object for every character in a file, think of it how many objects we will create for a long document. What will be the application performance.


How to Apply Flyweight

The object which we are going to create in high number should be analyzed before going for flyweight. Idea is to create lesser number of objects by reusing the same objects. Create smaller groups of objects and they should be reused by sharing. Closely look
at objects properties and they can be segregated as two types intrinsic and extrinsic. Sharing is judged with respect to a context. Lets take the example of editors.

Consider a simple text editor where we can use only alphabet set A to Z. If we are going to create 100 page document using this editor we may have 200000 (2000 X 100) characters (assuming 2000 characters / page). Without flyweight we will create 200000 objects
to have fine grained control. With such fine control, every character can have its own characteristics like color, font, size, etc. How do we apply flyweight here?


Intrinsic and Extrinsic State

Create only 26 objects for (A to Z) mapping every unique characters. These 26 objects will have intrinsic state as its character. That is object ‘a’ will have state as character ‘a’. Then what happens to color, font and size? Those are the extrinsic state and
will be passed by client code. 26 objects will be in store, client code will get the needed character/object and pass the extrinsic state to it with respect to the context. With respect to context means, ‘a’ in first line may come in red color and the same
character may come in blue color in different line.


Flyweight Implementation

The object with intrinsic state is called flyweight object. When we implement flyweight we create concrete objects and have the intrinsic state stored in that. To create those concrete objects we will have factory and that is called Flyweight factory. This
factory is to ensure that the objects are shared and we don’t end up creating duplicate objects.

Let us take an example scenario of drawing. We need to draw different geometrical shapes like rectangles and ovals in huge number. Every shape may vary in colour, size, fill type, font used. For implementation sake lets limit our shapes to two rectangle and
oval. Every shape will be accompanied by a label which directly maps it with the shape. That is all rectangles will have label as ‘R’ and all ovals will have label as ‘O’.

Now our flyweight will have intrinsic state as label only. Therefore we will have only two flyweight objects. The varying properties colour, size, fill type and font will be extrinsic. We will have a flyweight factory that will maintain the two flyweight objects
and distribute to client accordingly. There will be an interface for the flyweights to implement so that we will have a common blueprint and that is the flyweight interface.

Client code will use random number generators to create extrinsic properties. We are not storing the extrinsic properties anywhere, we will calculate on the fly and pass it. Use of random number generator is for convenience.


UML for Flyweight




Java Code Implementation for Flyweight

import java.awt.Color;
import java.awt.Graphics;

public interface MyShape {
public void draw(Graphics g, int x, int y, int width, int height,
Color color, boolean fill, String font);
}


import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

public class MyOval implements MyShape {

private String label;

public MyOval(String label) {
this.label = label;

}

public void draw(Graphics oval, int x, int y, int width, int height,
Color color, boolean fill, String font) {
oval.setColor(color);
oval.drawOval(x, y, width, height);
oval.setFont(new Font(font, 12, 12));
oval.drawString(label, x + (width / 2), y);
if (fill)
oval.fillOval(x, y, width, height);
}
}


import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

public class MyRectangle implements MyShape {

private String label;

public MyRectangle(String label) {
this.label = label;

}

public void draw(Graphics rectangle, int x, int y, int width, int height,
Color color, boolean fill, String font) {
rectangle.setColor(color);
rectangle.drawRect(x, y, width, height);
rectangle.setFont(new Font(font, 12, 12));
rectangle.drawString(label, x + (width / 2), y);
if (fill)
rectangle.fillRect(x, y, width, height);
}
}


import java.util.HashMap;

public class ShapeFactory {

private static final HashMap shapes = new HashMap();

public static MyShape getShape(String label) {
MyShape concreteShape = (MyShape) shapes.get(label);

if (concreteShape == null) {
if (label.equals("R")) {
concreteShape = new MyRectangle(label);
} else if (label.equals("O")) {
concreteShape = new MyOval(label);
}
shapes.put(label, concreteShape);
}
return concreteShape;
}
}


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Client extends JFrame {

private static final int WIDTH = 400;
private static final int HEIGHT = 400;

private static final String shapes[] = { "R", "O" };
private static final Color colors[] = { Color.red, Color.green, Color.blue };
private static final boolean fill[] = { true, false };
private static final String font[] = { "Arial", "Courier" };

public Client() {
Container contentPane = getContentPane();

JButton startButton = new JButton("Draw Shapes");
final JPanel panel = new JPanel();

contentPane.add(panel, BorderLayout.CENTER);
contentPane.add(startButton, BorderLayout.SOUTH);
setSize(WIDTH, WIDTH);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);

startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();
for (int i = 0; i < 100; ++i) {
MyShape shape = ShapeFactory.getShape(getRandomShape());
shape.draw(g, getRandomX(), getRandomY(), getRandomWidth(),
getRandomHeight(), getRandomColor(),
getRandomFill(), getRandomFont());
}
}
});
}

private String getRandomShape() {
return shapes[(int) (Math.random() * shapes.length)];
}

private int getRandomX() {
return (int) (Math.random() * WIDTH);
}

private int getRandomY() {
return (int) (Math.random() * HEIGHT);
}

private int getRandomWidth() {
return (int) (Math.random() * (WIDTH / 7));
}

private int getRandomHeight() {
return (int) (Math.random() * (HEIGHT / 7));
}

private Color getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}

private boolean getRandomFill() {
return fill[(int) (Math.random() * fill.length)];
}

private String getRandomFont() {
return font[(int) (Math.random() * font.length)];
}

public static void main(String[] args) {
Client client = new Client();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: