Java中的Flyweight设计模式
今天,我们将研究Flyweight设计模式。
轻量化设计模式
根据GoF,flyweight设计模式的意图是:
<p>Use sharing to support large numbers of fine-grained objects efficiently</p>
Flyweight设计模式是一种结构设计模式,例如Facade模式,Adapter模式和Decorator模式。
当我们需要创建一个类的许多对象时,可以使用Flyweight设计模式。
由于每个对象都会占用对于低内存设备(例如移动设备或者嵌入式系统)至关重要的内存空间,因此可以应用flyweight设计模式来通过共享对象来减少内存负载。
在应用flyweight设计模式之前,我们需要考虑以下因素:
应用程序要创建的对象数量应该很大。
对象的创建占用大量内存,并且也很耗时。
对象属性可以分为内部属性和外部属性,对象的外部属性应由客户端程序定义。
要应用flyweight模式,我们需要将Object属性分为内部属性和外部属性。
内部属性使对象唯一,而外部属性由客户端代码设置并用于执行不同的操作。
例如,对象圆可以具有外部属性,例如颜色和宽度。
为了应用flyweight模式,我们需要创建一个Flyweight工厂来返回共享对象。
对于我们的示例,假设我们需要使用线条和椭圆形创建图形。
因此,我们将有一个"形状"接口及其具体实现,如"线"和"椭圆"。
椭圆类将具有固有属性,以确定是否用给定的颜色填充椭圆,而Line将不具有任何固有属性。
Flyweight设计模式界面和具体程序
Shape.java
package com.theitroad.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public interface Shape {
public void draw(Graphics g, int x, int y, int width, int height,
Color color);
}
Line.java
package com.theitroad.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Line implements Shape {
public Line(){
System.out.println("Creating Line object");
//adding time delay
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void draw(Graphics line, int x1, int y1, int x2, int y2,
Color color) {
line.setColor(color);
line.drawLine(x1, y1, x2, y2);
}
}
椭圆形Java
package com.theitroad.design.flyweight;
import java.awt.Color;
import java.awt.Graphics;
public class Oval implements Shape {
//intrinsic property
private boolean fill;
public Oval(boolean f){
this.fill=f;
System.out.println("Creating Oval object with fill="+f);
//adding time delay
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void draw(Graphics circle, int x, int y, int width, int height,
Color color) {
circle.setColor(color);
circle.drawOval(x, y, width, height);
if(fill){
circle.fillOval(x, y, width, height);
}
}
}
注意,我在创建具体类的对象时故意引入了延迟,以使flyweight模式可用于实例化时花费大量时间的对象。
轻量化工厂
flyweight工厂将由客户端程序用来实例化Object,因此我们需要在工厂中保留一个Object映射,客户端应用程序不应访问该Map。
每当客户端程序调用以获取Object的实例时,都应从HashMap返回它,如果找不到,则创建一个新的Object并放入Map中,然后返回它。
我们需要确保在创建对象时考虑所有固有属性。
我们的flyweight工厂类看起来像下面的代码。
ShapeFactory.java
package com.theitroad.design.flyweight;
import java.util.HashMap;
public class ShapeFactory {
private static final HashMap<ShapeType,Shape> shapes = new HashMap<ShapeType,Shape>();
public static Shape getShape(ShapeType type) {
Shape shapeImpl = shapes.get(type);
if (shapeImpl == null) {
if (type.equals(ShapeType.OVAL_FILL)) {
shapeImpl = new Oval(true);
} else if (type.equals(ShapeType.OVAL_NOFILL)) {
shapeImpl = new Oval(false);
} else if (type.equals(ShapeType.LINE)) {
shapeImpl = new Line();
}
shapes.put(type, shapeImpl);
}
return shapeImpl;
}
public static enum ShapeType{
OVAL_FILL,OVAL_NOFILL,LINE;
}
}
注意在getShape方法中使用Java Enum进行类型安全,Java Composition(形状映射)和Factory模式。
Flyweight设计模式客户端示例
下面是使用flyweight模式实现的示例程序。
DrawingClient.java
package com.theitroad.design.flyweight;
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;
import com.theitroad.design.flyweight.ShapeFactory.ShapeType;
public class DrawingClient extends JFrame{
private static final long serialVersionUID = -1350200437285282550L;
private final int WIDTH;
private final int HEIGHT;
private static final ShapeType shapes[] = { ShapeType.LINE, ShapeType.OVAL_FILL,ShapeType.OVAL_NOFILL };
private static final Color colors[] = { Color.RED, Color.GREEN, Color.YELLOW };
public DrawingClient(int width, int height){
this.WIDTH=width;
this.HEIGHT=height;
Container contentPane = getContentPane();
JButton startButton = new JButton("Draw");
final JPanel panel = new JPanel();
contentPane.add(panel, BorderLayout.CENTER);
contentPane.add(startButton, BorderLayout.SOUTH);
setSize(WIDTH, HEIGHT);
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 < 20; ++i) {
Shape shape = ShapeFactory.getShape(getRandomShape());
shape.draw(g, getRandomX(), getRandomY(), getRandomWidth(),
getRandomHeight(), getRandomColor());
}
}
});
}
private ShapeType 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/10));
}
private int getRandomHeight() {
return (int) (Math.random() * (HEIGHT/10));
}
private Color getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
public static void main(String[] args) {
DrawingClient drawing = new DrawingClient(500,600);
}
}
我使用随机数生成在框架中生成不同类型的Shape。
如果在客户端程序上运行,您会注意到创建第一个Line Object和Oval对象时填充为true和false的延迟。
之后,由于程序使用共享库,因此程序将快速执行。
多次单击"绘制"按钮后,框架如下图所示。
并且您将在命令行中看到以下输出,确认已共享对象。
Creating Line object Creating Oval object with fill=true Creating Oval object with fill=false
多数民众赞成在flyweight模式,我们将在以后的文章中探讨更多的设计模式。
如果喜欢,请在注释部分中分享您的想法,并与他人分享。
JDK中的Flyweight设计模式示例
所有包装器类的valueOf()方法都使用缓存的对象,这些对象显示了Flyweight设计模式的使用。
最好的示例是Java String类String Pool实现。

