享元模式
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用来提高性能。
UML:
享元模式 ≈ 工厂模式 + 单例模式(提供对象池)+ 组合模式(组合非享元对象)
享元模式的结构:
两种状态:
- 内部状态:存储在享元内部,不会随环境的改变而有所不同,是可以共享的。
- 外部状态:对象得以依赖的标记,是随环境的改变而改变的,不可共享的状态,因此外部状态是由客户端来保持(因为环境的变化是由客户端引起的)
主要角色:
- Flyweight(抽象享元角色):为具体享元规范需要实现的接口,定义外部状态和内部状态,非享元的外部状态以参数的形式通过方法传入
- ConcreteFlyweight(具体享元角色):实现抽象享元的接口
- UnsharableFlyweight(非享元角色):是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
- FlyweightFactory(享元工厂):负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
Cast:以五子棋、围棋为例,棋子的类型(内部状态)只有黑白两种,但是棋子的坐标(外部状态)是不同的,棋子对象可以减少到两个实例,而坐标由客户端控制。
AbstractChess:

1 public abstract class AbstractChess { 2 // 非享元角色(坐标)以参数形式调用 3 public abstract void show(int[][] coordinates); 4 }
Chess:

1 public class Chess extends AbstractChess { 2 private int type; 3 4 public Chess() { 5 } 6 7 public Chess(int type) { 8 this.type = type; 9 } 10 11 public String getChess(int type) { 12 if (type == 1) 13 return "○ "; 14 else if (type == -1) 15 return "● "; 16 return null; 17 } 18 19 public void show(int[][] coordinates) { 20 for (int i = 0; i < 10; i++) { 21 for (int j = 0; j < 10; j++) { 22 switch (coordinates[i][j]) { 23 case -1: 24 case 1: 25 System.out.print(getChess(coordinates[i][j])); 26 break; 27 default: 28 System.out.print(". "); 29 } 30 } 31 System.out.println(); 32 } 33 } 34 }
ChessFactory:

1 public class ChessFactory { 2 // 棋子池 3 private static Map<Integer, Chess> chessPool = new HashMap<>(); 4 5 public static Chess getChessBean(int key) { 6 if (chessPool.containsKey(key)) { 7 return chessPool.get(key); 8 } 9 if (key == -1) { 10 chessPool.put(-1, new Chess(key)); 11 } else if (key == 1) { 12 chessPool.put(1, new Chess(key)); 13 } else { 14 return null; 15 } 16 return chessPool.get(key); 17 } 18 public int getChessCount(){ 19 return chessPool.size(); 20 } 21 }
Client:(随机落子)

1 class Chessboard { 2 private Random rand = new Random(); 3 private int[][] board = new int[15][15]; 4 5 void init() { 6 for (int i = 0; i < 10; i++) { 7 int x, y, key = i % 2; 8 if (key == 0) key--; 9 Chess c = ChessFactory.getChessBean(key); 10 while (board[x = rand.nextInt(15)][y = rand.nextInt(15)] == 0) { 11 board[x][y] = key; 12 } 13 } 14 } 15 16 public int[][] getBoard() { 17 return board; 18 } 19 } 20 21 public class Client { 22 23 public static void main(String[] args) { 24 // 棋子工厂类 25 ChessFactory cf = new ChessFactory(); 26 // 初始化棋盘 27 Chessboard cb = new Chessboard(); 28 cb.init(); 29 System.out.println("ChessFactory.getChessCount() = " + cf.getChessCount()); 30 AbstractChess ch1 = cf.getChessBean(1); 31 AbstractChess ch2 = cf.getChessBean(-1); 32 ch1.show(cb.getBoard()); 33 } 34 }
运行结果:
特点:
优点:运用共享技术有效地支持大量细粒度的对象的复用,提供一个对象池以提高性能、节约内存开销。
缺点:为了使对象可以被共享,需要将不能共享的对象外部化,使得系统变得较为复杂,增加理解难度。
PS:享元模式的享元工厂类中通常使用简单工厂模式来生成享元对象,在一个系统中,通常只有唯一一个享元工厂,因此享元工厂类可以使用单例模式进行设计;
享元模式可以结合组合模式形成复合享元模式,统一对享元对象设置外部状态。
JDK源码:
Integer中的享元模式: