Java设计模式--享元模式

享元模式

运用共享技术有效的支持大量细粒度的对象。

Flyweight Pattern

Use sharing to support large numbers of fine-grained objects efficiently.

类图

模式的结构与使用

享元方法模式的结构中包括三种角色。

  • 享元接口(Flyweight):是一个接口,该接口定义了享元对外公开其内部数据的方法,以及享元接收外部数据的方法。
  • 具体享元(Concrete Flyweight):实现享元接口的类,该类的实例称为享元对象,或简称享元。具体享元类的成员变量为享元对象的内部状态,享元对象的内部状态必须与所处的周围环境无关,即要保证使用享元对象的应用程序无法更改享元的内部状态,只有这样才能使得享元对象在系统中被共享。因为享元对象是用来共享的,所以不能允许用户各自地使用具体享元类来创建对象,这样就无法达到共享的目的,因为不同用户用具体享元类创建的对象显然是不同的,所以,具体享元类的构造方法必须是private的,其目的是不允许用户程序直接使用具体享元类来创建享元对象,创建和管理享元对象由享元工厂负责。
  • 享元工厂(Flyweight Factory):享元工厂是一个类,该类的实例负责创建和管理享元对象,用户或其他对象必须请求享元工厂为它得到一个享元对象。享元工厂可以通过一个散列表(也称共享池)来管理享元对象,当用户程序或其他若干个对象向享元工厂请求一个享元对象时,如果享元工厂的散列表中已有这样的享元对象,享元工厂就提供这个享元对象给请求者,否则就创建一个享元对象添加到散列表中,同时该享元对象提供给请求者。显然,当若干个用户或对象请求享元工厂提供一个享元对象时,第一个用户获得该享元对象的时间可能慢一些,但是后继的用户会较快地获得这个享元对象。可以使用单例模式来设计享元工厂,即让系统中只有一个享元工厂的实例。另外,为了让享元工厂能生成享元对象,需要将具体享元类作为享元工厂的内部类。

简单的例子

Flyweight的抽象类Flyweight.java

1
2
3
4
5
6
7
8
package Flyweight;
public interface Flyweight {
public double getHeight(); //返回内部数据
public double getWidth();
public double getLength();
public void printMess(String mess); //使用参数mess获取外部数据
}

Flyweight Factory的实现类FlyweightFactory.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
69
package Flyweight;
import java.util.HashMap;
public class FlyweightFactory {
private HashMap<String, Flyweight> hashMap;
static FlyweightFactory factory = new FlyweightFactory();
public FlyweightFactory() {
this.hashMap = new HashMap<String, Flyweight>();
}
public static FlyweightFactory getFactory() {
return factory;
}
public synchronized Flyweight getFlyweight(String key) {
if (hashMap.containsKey(key)) {
return hashMap.get(key);
} else {
double width = 0,height = 0,lenght = 0;
String[] str = key.split("#");
width = Double.parseDouble(str[0]);
height = Double.parseDouble(str[1]);
lenght = Double.parseDouble(str[2]);
Flyweight ft = new ConcreteFlyweight(width, height, lenght);
hashMap.put(key, ft);
return ft;
}
}
class ConcreteFlyweight implements Flyweight {
private double width;
private double height;
private double lenght;
public ConcreteFlyweight(double width, double height, double lenght) {
this.width = width;
this.height = height;
this.lenght = lenght;
}
@Override
public double getHeight() {
return height;
}
@Override
public double getWidth() {
return width;
}
@Override
public double getLength() {
return lenght;
}
@Override
public void printMess(String mess) {
System.out.println(mess);
System.out.print("宽度:" + width);
System.out.print("高度:" + height);
System.out.print("长度:" + lenght);
}
}
}

需要使用共享数据的类Car.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
package Flyweight;
public class Car {
Flyweight flyweight;
String name, color;
int power;
public Car(Flyweight flyweight, String name, String color, int power) {
this.flyweight = flyweight;
this.name = name;
this.color = color;
this.power = power;
}
public void print() {
System.out.println("名称:" + name);
System.out.println("颜色:" + color);
System.out.println("功率:" + power);
System.out.println("宽度:" + flyweight.getWidth());
System.out.println("高度:" + flyweight.getHeight());
System.out.println("长度:" + flyweight.getLength());
}
}

测试类Application.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
package Flyweight;
public class Application {
public static void main(String[] args) {
FlyweightFactory factory = FlyweightFactory.getFactory();
double width = 1.82, height = 1.47, lenght = 5.12;
String key = "" + width + "#" + height + "#" + lenght;
Flyweight flyweight = factory.getFlyweight(key);
Car audiA60ne = new Car(flyweight, "奥迪A6", "黑色", 128);
Car audiA6Two = new Car(flyweight, "奥迪A6", "灰色", 160);
//audiA60ne和audiA6Two没有向享元传递外部数据,而是获取享元的内部数据
audiA60ne.print();
audiA6Two.print();
width = 1.77;
height = 1.45;
lenght = 4.63;
key = "" + width + "#" + height + "#" + lenght;
Car audiA40ne = new Car(flyweight, "奥迪A4", "蓝色", 126);
Car audiA4Two = new Car(flyweight, "奥迪A4", "红色", 138);
//audiA40ne和audiA4Two向享元传递外部数据,这些数据是不共享的
flyweight.printMess("名称: 奥迪A4 颜色:蓝色 功率:126");
flyweight.printMess("名称: 奥迪A4 颜色:红色 功率:138");
}
}

执行结果

享元模式的优点

  • 使用享元模式可以节省内存的开销,特别适合处理大量细粒度对象,这些对象的许多属性值是相同的,而且一旦创建则不容许修改。
  • 享元模式中的享元可以使用方法的参数接收外部状态中的数据,但外部状态数据不会干扰到享元中的内部数据,这就使享元可以在不同的环境中被共享。

适用享元模式的情景

下列情况之一就可以考虑使用享元模式:

  • 一个应用程序使用大量的对象,这些对象之间部分属性本质上是相同的,这时应使用享元来封装相同的部分。
  • 对象的多数状态都可变为外部状态,就可以考虑将这些对象作为系统中的享元来使用。

下载源码请到

MyGitHub

文章目录
  1. 1. 享元模式
  2. 2. Flyweight Pattern
  3. 3. 类图
  4. 4. 模式的结构与使用
  5. 5. 简单的例子
    1. 5.1. Flyweight的抽象类Flyweight.java
    2. 5.2. Flyweight Factory的实现类FlyweightFactory.java
    3. 5.3. 需要使用共享数据的类Car.java
    4. 5.4. 测试类Application.java
  6. 6. 执行结果
  7. 7. 享元模式的优点
  8. 8. 适用享元模式的情景
    1. 8.1. 下列情况之一就可以考虑使用享元模式:
  9. 9. 下载源码请到