设计模式之享元模式

设计模式之享元模式

今天,我们将研究Flyweight设计模式。

什么是享元设计模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,例如Facade模式,Adapter模式和Decorator模式。它提供了减少对象数量从而改善应用所需的对象结构的方式。并且使用共享有效地支持大量细粒度的对象。

当我们需要创建一个类的许多对象时,可以使用Flyweight设计模式。 由于每个对象都会占用对于低内存设备(例如移动设备或嵌入式系统)至关重要的内存空间,因此可以应用轻量级设计模式来通过共享对象来减少内存负载。

享元设计模式使用场景

在应用flyweight设计模式之前,我们需要考虑以下因素:

  • 应用程序要创建的对象数量应该很大。
  • 对象的创建占用大量内存,并且也很耗时。
  • 对象属性可以分为内部属性和外部属性,对象的外部属性应由客户端程序定义。

要应用flyweight模式,我们需要将Object属性分为内部属性和外部属性。 内部属性使对象唯一,而外部属性由客户端代码设置并用于执行不同的操作。 例如,Circle对象可以具有外部属性,例如颜色和宽度。

为了应用flyweight模式,我们需要创建一个Flyweight factory工厂来返回共享对象。 对于我们的示例,假设我们需要使用线条和椭圆形创建图形。 因此,我们将有一个Shape界面及其具体实现,如LineOval。 椭圆类将具有固有属性,以确定是否用给定的颜色填充椭圆,而Line将不具有任何固有属性。

Flyweight设计模式代码示例

Shape.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.awt.Graphics;
import java.awt.Color;

/**
* 图形接口
* @author Mr.zxb
* @date 2020-09-19 19:36:10
*/
public interface Shape {
/**
* 绘画方法
* @param g
* @param x
* @param y
* @param width
* @param height
* @param color
*/
void draw(Graphics g, int x, int y, int width, int height, Color color);
}

Line.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.awt.Graphics;
import java.awt.Color;
import java.util.concurrent.TimeUnit;

/**
* 线
* @author Mr.zxb
* @date 2020-09-19 19:37:13
*/
public class Line implements Shape {

public Line() {
System.out.println("Creating Line object.");
// adding time delay
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

@Override
public void draw(Graphics line, int x, int y, int width, int height, Color color) {
line.setColor(color);
line.drawLine(x, y, width, height);
}
}

Oval.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.awt.Graphics;
import java.awt.Color;
import java.util.concurrent.TimeUnit;

/**
* 椭圆形
* @author Mr.zxb
* @date 2020-09-19 19:37:30
*/
public class Oval implements Shape {

/**
* intrinsic property
*/
private boolean fill;

public Oval(boolean fill) {
this.fill = fill;
System.out.println("Creating Oval object with fill = " + fill);

// adding time delay
try {
TimeUnit.SECONDS.sleep(2);
} 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 Factory

flyweight工厂将由客户端程序用来实例化Object,因此我们需要在工厂中保留一个Object映射,客户端应用程序不应访问该Map。

每当客户端程序调用以获取Object的实例时,都应从HashMap返回它,如果找不到,则创建一个新的Object并放入Map中,然后返回它。 我们需要确保在创建对象时考虑所有固有属性。

我们的flyweight工厂类看起来像下面的代码。

ShapeFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.HashMap;
import java.util.Map;

/**
* 图形工厂方法
* @author Mr.zxb
* @date 2020-09-19 19:44:23
*/
public class ShapeFactory {
public static final Map<ShapeType, Shape> SHAPES = new HashMap<>();

public static Shape getShape(ShapeType type) {
Shape shape = SHAPES.get(type);
if (shape == null) {
if (type.equals(ShapeType.OVAL_FILL)) {
shape = new Oval(true);
} else if (type.equals(ShapeType.OVAL_NOFILL)) {
shape = new Oval(false);
} else if (type.equals(ShapeType.LINE)) {
shape = new Line();
}
SHAPES.put(type, shape);
}
return shape;
}
}

请注意,在getShape()方法中将Java Enum用于类型安全,Java Composition(形状映射)和Factory模式。

ShapeType.java

1
2
3
4
5
public enum ShapeType {
OVAL_FILL,
OVAL_NOFILL,
LINE;
}

Flyweight设计模式客户端示例

下面是使用flyweight模式实现的示例程序。

DrawingClient.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import javax.swing.*;
import java.awt.*;

/**
* @author Mr.zxb
* @date 2020-09-19 19:49:04
*/
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) throws HeadlessException {
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(e -> {
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 drawingClient = new DrawingClient(500, 600);
}
}

我已经使用随机数生成在框架中生成不同类型的Shape。

如果在客户端程序上运行,您会注意到创建第一个Line和Oval对象时填充为true和false的延迟。 之后,由于程序使用共享库,因此程序将快速执行。

多次单击“绘制”按钮后,框架如下图所示。

并且您将在命令行中看到以下输出,确认已共享对象。

1
2
3
Creating Oval object with fill = false
Creating Line object.
Creating Oval object with fill = true

JDK中的Flyweight设计模式示例

所有包装器类的valueOf()方法都使用缓存的对象,这些对象显示了Flyweight设计模式的使用。 最好的示例是Java StringString Pool实现。

Flyweight设计模式要点

  • 在我们的示例中,没有强制客户端代码使用Flyweight工厂创建对象,但是我们可以强制这样做以确保客户端代码使用Flyweight模式实现,但这是针对特定应用程序的完整设计决策。
  • Flyweight模式引入了复杂性,如果共享对象的数量巨大,那么内存和时间之间就需要进行权衡,因此我们需要根据我们的要求明智地使用它。
  • 当Object的固有属性数量巨大时,Flyweight模式实现就没有用,这使得Factory类的实现变得复杂。

这就是Java中的Flyweight设计模式。