黑白五子棋或者围棋只有两种颜色-黑白,如果我们把棋子作为一个抽象类Chess,黑棋BlackChess和白棋WhiteChess分别作为继承抽象类的具体类,那么每下一步都需要new一个新的棋子对象,如此下来会产生大量的黑白棋对象。仔细观察黑白棋,不难发现黑白棋对象其实都一样,唯一不同的是其位置的变化。那么是否有一种方法可以实现这样的效果:不用创建大量的黑白棋对象,但是也能准确的实现其位置的变化?答案是:有的。
抽象棋子类Chess:
public abstract class Chess {
private String type; // 棋子种类,分黑两种
public Chess(String type){
this.type = type;
}
public String getType(){
return type;
}
// 对棋子的操作
public abstract void operation(Local local);
}
黑白棋子类BlackWhiteChess:
public class BlackWhiteChess extends Chess {
public BlackWhiteChess(String type) {
super(type);
}
@Override
public void operation(Local local) { // Local表示位置类
System.out.println(getType() + " " + local);
}
}
位置类Local:
public class Local {
private int x;
private int y;
public Local(int x, int y){
this.x = x;
this.y = y;
}
// 省略get和set方法
@Override
public String toString() {
return "横:" + x + ", 纵:" + y ;
}
}
棋子工厂类:
public class ChessFactory {
private static ChessFactory chessFactory;
public ConcurrentMap<String, Chess> flyWeightMap; // 共享池
private ChessFactory(){
flyWeightMap = new ConcurrentHashMap<String, Chess>();
}
// 双重锁单例模式获取棋子工厂类
public static ChessFactory getChessFactory() {
synchronized (ChessFactory.class) {
if (chessFactory == null){
synchronized (ChessFactory.class) {
chessFactory = new ChessFactory();
}
}
}
return chessFactory;
}
// 棋子工厂根据color构造不同的对象,并放入共享池
public Chess getChess(String color){
if (!flyWeightMap.containsKey(color)) {
flyWeightMap.put(color, new BlackWhiteChess(color));
}
return flyWeightMap.get(color);
}
}
客户端类Client:
public class Client {
public static void main(String[] args) {
String black = "black";
String white = "white";
ChessFactory chessFactory = ChessFactory.getChessFactory(); // 共享池
Chess chess, _chess, chess2, _chess2; // 棋子
chess = chessFactory.getChess(black); // 黑棋
_chess = chessFactory.getChess(black); // 黑棋
chess.operation(new Local(5, 5));
_chess.operation(new Local(5, 6));
System.out.println(chessFactory.flyWeightMap.size());
chess2 = chessFactory.getChess(white); // 白棋
_chess2 = chessFactory.getChess(white); // 白棋
chess2.operation(new Local(6, 6));
_chess2.operation(new Local(7, 7));
System.out.println(chessFactory.flyWeightMap.size());
}
}
运行结果:
black 横:5, 纵:5
black 横:5, 纵:6
1
white 横:6, 纵:6
white 横:7, 纵:7
2
结果显示黑白棋子虽然有4颗,但共享池中的实际对象只有2个,即一个黑棋对象和一个白棋对象。实例代码的UML类图为:
这里的关键在于ChessFactory中的getChess(String)方法。根据方法参数color不同构造不同对象,并把对象放入线程安全的共享池,客户端需要的时候直接从共享池中获取即可,达到对象共享的目的;另外,把位置信息作为方法参数传入对象,达到不同状态的目的。这种共享元对象的代码结构称为享元模式。
《大话设计模式》中这样定义享元模式:运用共享技术有效的支持大量细粒度的对象。
问题1:共享技术是指什么?细粒度的对象是什么?
共享技术是一种通过共享池避免重复生成大量的相似对象的技术。
细粒度的对象是指实现一个接口的类的对象,使用过程中会大量产生的对象,对象之间有大量重复的不可变的信息(类型),只有少量可变的信息(位置)。
问题2:如何支持(实现)细粒度的对象共享?
在享元模式中,把对象内部不可变的信息(如类型)称为内部状态,把少量可变的信息(如位置)提取到对象外部,如客户端来决定可变信息,这种可变信息称为外部状态。所以,共享是根据内部状态把对象放入共享池内,客户端传入外部状态和内部状态来获取共享池内的对象。
享元模式的UML类图:
享元模式各角色介绍:
Flyweight:所有具体享元类的抽象类或者接口,通过这个接口,Flyweight可以接受并作用于外部状态。
ConcreteFlyweight:继承Flyweight抽象类或者接口,并为内部状态增加存储空间;
UnsharedConcreteFlyweight:继承Flyweight抽象类或者接口,指那些不需要共享的Flyweight子类。因为Flyweight接口共享成为可能,但他并不强制共享。
FlyweightFactory:一个享元工厂,用来创建并管理Flyweight对象,它主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。
PS:实例中并没有UnsharedConcreteFlyweight类,非共享的类根据实际情况来定。
避免重复创建对象,节省内存空间。根据内部状态把对象存储在共享池,需要时去共享池取就行。
借用设计模式书中的话就是:如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。
《大话设计模式》
https://www.cnblogs.com/lfxiao/p/6817141.html
https://blog.csdn.net/nobody_1/article/details/85415892