什么是享元模式
Flyweight 运行共享技术有效地支持大量细粒度的对象。
享元的核心思想:如果一个对象实例一旦创建就不可变,那么反复创建相同的实例就没有必要,直接向调用方返回一个共享的实例就行,这样即节省内存,又可以减少创建对象的过程,提高运行速度。享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
模式角色
1)抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外部状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
内部状态:是指对象共享出来的信息,存储在享元对象内部,且不会随环境的改变而改变。
外部状态:对象得以依赖的一个标记,是随环境的改变而改变,不可共享的状态。
2)ConcreteFlyweight具体享元()角色:实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
3) 复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称作不可共享的享元对象。这个角色一般很少使用。
4)享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
UML类图
![](https://img.haomeiwen.com/i10195209/a40a0bc1b5ee5124.png)
代码实现
以围棋为例,一盘棋的棋子大小、材质、颜色(黑/白)往往都是确定的也就是上面说的内部状态。而围棋落子的位置却不一定外部状态, 因此我们可以将棋子位置从棋子对象中剥离,然后让棋子对象共享大小、材质、颜色属性, 并在调用时将位置传入, 就可大大减少子对象的所占据的内存空间。
public interface Flyweight {
// Position外部状态
void playGo(Position position);
}
public class Position {
private Integer x;
private Integer y;
public Position(Integer x, Integer y) {
this.x = x;
this.y = y;
}
public Integer getX() {
return x;
}
public void setX(Integer x) {
this.x = x;
}
public Integer getY() {
return y;
}
public void setY(Integer y) {
this.y = y;
}
@Override
public String toString() {
return "Position{" +
"x=" + x +
", y=" + y +
'}';
}
}
public class WeiqiFlyweight implements Flyweight {
// 内部状态
private String color;
public WeiqiFlyweight(String color) {
this.color = color;
}
@Override
public void playGo(Position position) {
System.out.println(color + "棋子," + "下在" + position.getX() + "," + position.getY());
}
}
public class FlyweightFactory {
private Map<String, WeiqiFlyweight> cache = new HashMap<>();
public Flyweight getWeiqiFlyweight(String color) {
if (!cache.containsKey(color)) {
cache.put(color, new WeiqiFlyweight(color));
}
return cache.get(color);
}
/**
* 计算map大小
* @return
*/
public int getCount() {
return cache.size();
}
}
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight = factory.getWeiqiFlyweight("白色");
Position position = new Position( 3, 5);
flyweight.playGo(position);
Flyweight flyweight2 = factory.getWeiqiFlyweight("黑色");
Position position2 = new Position( 4, 5);
flyweight2.playGo(position2);
Flyweight flyweight3 = factory.getWeiqiFlyweight("白色");
Position position3 = new Position( 6, 5);
flyweight3.playGo(position3);
Flyweight flyweight4 = factory.getWeiqiFlyweight("黑色");
Position position4 = new Position( 5, 5);
flyweight4.playGo(position4);
System.out.println("map大小:" + factory.getCount());
}
}
白色棋子,下在3,5
黑色棋子,下在4,5
白色棋子,下在6,5
黑色棋子,下在5,5
map大小:2
优点
大幅度地降低内存中对象的数量,减缓堆内存压力。
缺点
享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
总的来说,享元模式一般是解决系统性能问题的,所以经常用于底层开发,在项目开发中并不常用。
享元模式完整代码
网友评论