享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用来提高性能。

UML:

享元模式 ≈ 工厂模式 + 单例模式(提供对象池)+ 组合模式(组合非享元对象)

享元模式的结构:

两种状态:

  • 内部状态:存储在享元内部,不会随环境的改变而有所不同,是可以共享的。
  • 外部状态:对象得以依赖的标记,是随环境的改变而改变的,不可共享的状态,因此外部状态是由客户端来保持(因为环境的变化是由客户端引起的)

主要角色:

  • Flyweight(抽象享元角色):为具体享元规范需要实现的接口,定义外部状态和内部状态,非享元的外部状态以参数的形式通过方法传入
  • ConcreteFlyweight(具体享元角色):实现抽象享元的接口
  • UnsharableFlyweight(非享元角色):是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
  • FlyweightFactory(享元工厂):负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

Cast:以五子棋、围棋为例,棋子的类型(内部状态)只有黑白两种,但是棋子的坐标(外部状态)是不同的,棋子对象可以减少到两个实例,而坐标由客户端控制。

AbstractChess:

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

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 }
View Code

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 }
View Code

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 }
View Code

运行结果:

特点:

优点:运用共享技术有效地支持大量细粒度的对象的复用,提供一个对象池以提高性能、节约内存开销

缺点:为了使对象可以被共享,需要将不能共享的对象外部化,使得系统变得较为复杂,增加理解难度。

PS:享元模式的享元工厂类中通常使用简单工厂模式来生成享元对象,在一个系统中,通常只有唯一一个享元工厂,因此享元工厂类可以使用单例模式进行设计

享元模式可以结合组合模式形成复合享元模式,统一对享元对象设置外部状态。


JDK源码:

Integer中的享元模式:

 

posted @ 2020-09-15 10:21  whyha  阅读(200)  评论(0)    收藏  举报