设计模式之享元模式

一、什么是享元模式

享元模式,运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来最大程度减少需要创建的对象数量,避免大量相似对象的开销,从而提高系统资源的利用率。

享元模式,也叫作蝇量模式,属于结构型设计模式。

二、享元模式类图

设计模式之享元模式插图

三、享元模式角色

  • 抽象享元角色(FlyWeight): 要求是一个抽象类或接口,声明抽象方法,这些方法需要具体享元类来实现,并向外界提供享元对象的内部状态数据及外部状态数据。
  • 具体享元角色(ConcreteFlyWeight): 它继承或实现了抽象享元类,称为享元对象,需要通过享元工厂类来提供唯一的享元对象。
  • 非享元角色(UnshareConcreteFlyWeight): 不能被共享的子类可设计为非共享具体享元类,它代表了享元对象的外部状态。非享元对象可以直接作为享元对象方法的参数进行传递。
  • 享元工厂角色(FlyWeightFactory): 负责创建和管理享元对象,客户端直接通过享元工厂对象来获取所需要的享元对象。

四、享元模式两种状态

  • 内部状态:不会随环境改变而改变的可共享部分。比如围棋的颜色就两种(白棋、黑棋)。
  • 外部状态:随环境改变而改变的不可以共享的部分。比如围棋的位置(也就是坐标)。

五、享元模式优缺点

优点:

  1. 极大减少内存中共享对象的数量,节约内存资源,提高系统性能。
  2. 享元模式中外部状态相对独立,且不影响内部状态。

缺点:

  1. 需要将享元对象的状态,分离为内部状态和外部状态,程序逻辑变得复杂了。

六、享元模式demo示例

1、Demo需求

以围棋为例,通过享元模式来实现,其中把白棋、黑棋作为具体享元类,白色、黑色是内部状态,围棋的坐标位置是外部状态。

2、数据类定义

/**
  * @Description: 非享元角色类(外部状态,棋子坐标)
  **/ public class GoChessPoint {

     // x 坐标
     private double x;
     // y 坐标
     private double y;
 
     public GoChessPoint(double x, double y) {
         this.x = x;
         this.y = y;
     }
 
     @Override
     public String toString() {
         return "棋子坐标{" +
                 "x=" + x +
                 ", y=" + y +
                 '}';
     }
 }

3、抽象类定义

/**
  * @Description: 抽象享元角色:围棋类
  **/ public abstract class AbstractGoChess {

     public abstract void show(GoChessPoint point);

 }

4、具体化对象类

/**
  * @Description: 具体享元角色类:白棋
  **/ public class WhiteGoChess extends AbstractGoChess {

     @Override
     public void show(GoChessPoint point) {
         System.out.println("白棋, " + point);
     }
 }
/**
  * @Description: 具体享元角色类:黑棋
  **/ public class BlackGoChess extends AbstractGoChess {
 
     @Override
     public void show(GoChessPoint point) {
         System.out.println("黑棋, " + point);
     }
 }

5、工厂类定义

/**
  * @Description: 享元工厂角色:围棋工厂类
  **/ public class GoChessFactory {

     // 饿汉式模式实例化单例对象
     private static GoChessFactory goc = new GoChessFactory();
     // 存放白棋/黑棋对象
     private Map<String, AbstractGoChess> goChessMap = new HashMap<>();
 
     // 私有化默认构造方法
     private GoChessFactory() {
         goChessMap.put("白棋", new WhiteGoChess());
         goChessMap.put("黑棋", new BlackGoChess());
     }
 
     // 获取单例对象
     public static GoChessFactory GetInstance() {
         return goc;
     }
 
     /**
      * 获取棋子对象
      *
      * @param name
      * @return
      */     public AbstractGoChess getGoChess(String name) {
         return goChessMap.get(name);
     }
 }

6、测试

/**
  * @Description: 享元模式客户端:围棋客户端测试类
  **/ public class GoClient {

     public static void main(String[] args) {
         // 获取白棋对象
         AbstractGoChess white1 = GoChessFactory.GetInstance().getGoChess("白棋");
         AbstractGoChess white2 = GoChessFactory.GetInstance().getGoChess("白棋");
         // 获取黑棋对象
         AbstractGoChess black1 = GoChessFactory.GetInstance().getGoChess("黑棋");
         AbstractGoChess black2 = GoChessFactory.GetInstance().getGoChess("黑棋");
 
         // 展示白棋1
         white1.show(new GoChessPoint(11.5, 20.8));
         // 展示白棋2
         white2.show(new GoChessPoint(11.5, 20.8));
         System.out.println("white1==white2: " + (white1 == white2));
         System.out.println("===============================");
         // 展示黑棋1
         black1.show(new GoChessPoint(21.5, 26.7));
         // 展示黑棋2
         black2.show(new GoChessPoint(16.1, 28.3));
         System.out.println("black1==black2: " + (black1 == black2));
     }
 }
白棋, 棋子坐标{x=11.5, y=20.8}
白棋, 棋子坐标{x=11.5, y=20.8}
white1==white2: true
===============================
黑棋, 棋子坐标{x=21.5, y=26.7}
黑棋, 棋子坐标{x=16.1, y=28.3}
black1==black2: true

发表评论