设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)

  1. 为什么要用享元模式
  2. 内部状态与外部状态
  3. 应用场景
  4. 场景举例
  5. 总结

为什么要用享元模式

有大量对象可复用,在服务端减少接口的调用,在客户端减少内存的占用

内部状态与外部状态

了解享元模式就避不开内部状态与外部状态

内部状态:不随环境的变化而变化,在各个对象间共享

外部状态:随环境的改变而改变,由客户端传入

举例说明,在五子棋中,棋子的颜色就是内部状态,只有黑白两色,落子位置(坐标)是外部状态,每次位置都不一样

如果每一个棋子都是一个对象的话,一个棋盘可以放两三百个对象,极大的浪费内存空间,但如果使用享元模式,则可以很好的解决对象的开销问题

应用场景

数据库连接池,String常量池,缓冲池

比如在Integer的源码中有

1
2
3
4
5
6
7
8
9
10
static final Integer cache[]; //定义常量数组
static{
//....省略,cache的范围在-128~127之间
cache = new Integer[(high - low) + 1];
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

场景举例

按照棋子举例,类图比较简单就不画了

抽象接口

1
2
3
public interface Chess {
void draw(int x, int y);
}

黑白棋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BlackChess implements Chess {
private final String COLOR = "黑";

@Override
public void draw(int x, int y) {
System.out.println(COLOR + "棋子落在("+x+","+y+")处");
}
}
public class WhiteChess implements Chess {
private final String COLOR = "白";

@Override
public void draw(int x, int y) {
System.out.println(COLOR + "棋落在("+x+","+y+")处");
}
}

对象工厂

1
2
3
4
5
6
7
8
9
10
11
12
public class ChessFactory {
private static final Map<String,Chess> chessMap = new HashMap<>();

public static Chess getChess(String color) {
Chess chess = chessMap.get(color);
if (chess == null) {
chess = "黑".equals(color) ? new BlackChess() : new WhiteChess();
chessMap.put(color,chess);
}
return chess;
}
}

调用

1
2
3
4
5
6
7
8
9
10
11
12
Chess white1 = ChessFactory.getChess("白");
Chess white2 = ChessFactory.getChess("白");
Chess black1 = ChessFactory.getChess("黑");
Chess black2 = ChessFactory.getChess("黑");
white1.draw(1,3);
white2.draw(2,4);
black1.draw(3,3);
black2.draw(2,6);
System.out.println(white1.hashCode());
System.out.println(white2.hashCode());
System.out.println(black1.hashCode());
System.out.println(black2.hashCode());

结果输出

1
2
3
4
5
6
7
8
白棋落在(1,3)处
白棋落在(2,4)处
黑棋子落在(3,3)处
黑棋子落在(2,6)处
21685669
21685669
2133927002
2133927002

总结

要区分状态,内部状态不可以从客户端设置,而外部状态必须从客户端设置,该模式可以提高内存使用率