一个flyweight的好例子!

使用共享对象可有效地支持大量的细粒度的对象。

图1展示了Flyweight 设计模式的一个类图。

图1. Flyweight 类图

Flyweights可通过flyweight factory的例子来说明,这个flyweight factory创建了一定数量的flyweights并将他们定期的发放给客户端,一次发放一个。 这些flyweights 可根据某些标准显示。例如:你有一个知道如何划出直线的直线对象池。这时, flyweight factory就能为每一个直线颜色创建一个直线对象,如一个对象用于白线,另一个对象用于蓝线。这些线,也就是flyweights,只要你画白线或者画蓝线,他们就会重新使用。如果你要画1000条白线和6000条蓝线,实际上只显示两条线,而不是7000条线。

图2的流程图描述了客户端和flyweight factory的交互过程。

图2. Flyweight流程图

客户端程序并不直接显示flyweights;相反的它们只是从flyweight factory中读取flyweight。flyweight factory首先检查自己是否有符合特别标准(如,蓝线或者白线)的 flyweight ;如果有,就返回对该flyweight的访问。如果它不能找到符合标准的flyweight ,就会自己产生一个,加到共享池内,并将它返回给客户端程序。i

你可能不知道如何重新使用flyweights。例如:直线flyweight 如何才能画出所有这些位置和长度都不同的指定颜色的直线呢?要完成这个任务,你可将flyweight状态劈为两个状态:intrinsic 状态(如直线颜色)和extrinsic 状态(直线位置和长度)。这样flyweight 保存了内在状态,而外部状态必须在运行过程中传递给flyweight。 通过具体化外部状态,你可以轻松的重新使用适合于各种不同外部状态的flyweight。

例子演示

接下来本文将带你实现一个直线flyweight ,它与本文开头讨论过的直线flyweight 类似。我们将用三个方法来完成这件工作。第一,我不借助直线对象,只是重复地在图形语境内绘出10,000直线。第二,我实现一个不能共享的简单的Line 类。第三,我就这个简单的类变成flyweight。图5显示了重复描绘直线的应用程序。

图5 使用Swing描绘直线

不使用直线对象的制图

Example 3 列表显示了图5的应用程序。


Example 3. Drawing lines without line objects
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public 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 WINDOW_WIDTH=400,
WINDOW_HEIGHT=400,
NUMBER_OF_LINES=10000;

public Test() {
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());

JButton button = new JButton("draw lines");
final JPanel panel = new JPanel();

contentPane.add(panel, BorderLayout.CENTER);
contentPane.add(button, BorderLayout.SOUTH);
setBounds(20,20,WINDOW_WIDTH,WINDOW_HEIGHT);

button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();

for(int i=0; i < NUMBER_OF_LINES; ++i) {
g.setColor(getRandomColor());
g.drawLine(getRandomX(), getRandomY(),
getRandomX(), getRandomY());
}
}
});
}
public static void main(String[] args) {
Test test = new Test();
test.show();
}
private int getRandomX() {
return (int)(Math.random()*WINDOW_WIDTH);
}
private int getRandomY() {
return (int)(Math.random()*WINDOW_HEIGHT);
}
private Color getRandomColor() {
return colors[(int)(Math.random()*colors.length)];
}
}/>

/>

上述应用程序创建了带有两个面板的窗口。当你点击底部面板上的按钮时,该应用程序就会使用任意颜色,在任意位置,绘出任意长度的直线。这些直线是由图形语境中的drawLine() 方法描绘的。

接下来,我们不直接使用图形语境,而是实现一个类 Line ,用它来描绘直线。

简单类Line的实现

可能你会认为使用图形语境来描绘直线不是非常面向对象的,所以你想使用一个类Line 来封装这个功能。现在就让我们来完成这个功能的封装。 Example 4 列出的就是修改过的应用程序。

Example 4. Drawing lines with a heavyweight line object


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class Test extends JFrame{
public Test() {
...
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();

for(int i=0; i < NUMBER_OF_LINES; ++i) {
Color color = getRandomColor();
System.out.println("Creating " + color + " line");
Line line = new Line(color,
getRandomX(), getRandomY(),
getRandomX(), getRandomY());
line.draw(g);
}
}
});
}
...
}/>

/>

我只写出了相关的代码;程序的其余部分与Example 3中的相应部分一样。上述的应用程序创建了10,000 个直线对象并告诉每一个对象自我描绘。Example 5 列表显示了这个类Line 。

Example 5. A heavyweight Line class


import java.awt.*;

public class Line {
private Color color = Color.black;
private int x, y, x2, y2;

public Line(Color color, int x, int y, int x2, int y2) {
this.color = color;
this.x = x; this.y = y;
this.x2 = x2; this.y2 = y2;
}
public void draw(Graphics g) {
g.setColor(color);
g.drawLine(x, y, x2, y2);
}
}/>

/>

上述的类Line 保留了它的颜色和端点,并使用这些端点来描绘直线。

flyweight Line的实现

显然,我们不再按照上述方式创建10,000 直线,现在我们将直线的数量减为6条,一种颜色对应一条直线。Example 6 列表显示了修改后的应用程序。


Example 6. Drawing lines with a flyweight line object
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class Test extends JFrame {
...
public Test() {
...
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Graphics g = panel.getGraphics();

for(int i=0; i < NUMBER_OF_LINES; ++i) {
Line line = LineFactory.getLine(getRandomColor());
line.draw(g, getRandomX(), getRandomY(),
getRandomX(), getRandomY());
}
}
});
}
...
}/>

/>

差不多就这样了。现在直线对象可从直线工厂中获得,直线工厂返回6条共享直线中的一个。Example 7 列出了这个工厂。

Example 7. The line factory


import java.util.HashMap;
import java.awt.Color;

public class LineFactory {
private static final HashMap linesByColor = new HashMap();

public static Line getLine(Color color) {
Line line = (Line)linesByColor.get(color);

if(line == null) {
line = new Line(color);
linesByColor.put(color, line);
System.out.println("Creating " + color + " line");
}
return line;
}
}/>

/>

直线工厂保留了直线的HashMap,HashMap可键入颜色。当你调用getLine()函数向工厂请求一条直线时,工厂首先检查HashMap是否存在这种颜色的直线;若有的话,则返回直线。否则,工厂创建一个新的直线,把它保存在HashMap中,并返回该直线。无论是哪种情况,调用者都能得到一条共享的Line 对象。

Example 8 列出了修改后的类Line。

Example 8. A flyweight Line implementation


import java.awt.*;

public class Line {
private Color color;

public Line(Color color) {
this.color = color;
}
public void draw(Graphics g, int x, int y, int x2, int y2) {
g.setColor(color);
g.drawLine(x, y, x2, y2);
}
}/>

/>

注意上述类Line比 Example 5中的类 Line 要简单。为什么?因为上述的类Line 已经清除了外部状态即直线的端点。由于上述的类Line 不再保留外部状态,也由于外部状态从客户端传递到方法draw(),所以这些类的实例得以共享。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s