设计模式 - 享元模式

117 阅读3分钟

本文已参加【新人创作礼】活动,一起开启掘金创作之路。

概念

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,是一种对象结构型模式。

角色

  • 抽象享元(Flyweight)角色:该角色对享元类进行抽象,需要外部状态的操作可以通过参数的形式将外部状态传入。
  • 具体享元(ConcreteFlyweight)角色:该角色实现抽象享元定义的业务,注意享元对象的内部状态必须与环境无关,从而使得享元对象可以在系统内共享。
  • 享元工厂(FlyweightFactory)角色:该角色就是构造一个池容器,负责创建和管理享元角色,并提供从池容器中获得对象的方法,保证享元对象可以被系统适当的共享。当一个客户端对象请求一个享元对象时,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。如果已经有了,享元工厂则提供这个已有的享元对象;否则创建一个合适的享元对象。

image.png

样例代码

class FlyweightFactory {
    // 定义一个HashMap用于存储享元对象,实现享元池
    private HashMap<String, Flyweight> flyweights = new HashMap<>();

    public Flyweight getFlyweight(String key) {
        
        if (flyweights.containsKey(key)) {
            return flyweights.get(key);
        } else {
            Flyweight fw = new ConcreteFlyweight();
            flyweights.put(key, fw);
            return fw;
        }
    }
}

interface Flyweight {
    // 传入外部状态
    public void operation(String extrinsState);
}

public class ConcreteFlyweight implements Flyweight {
    // 内部状态,同一个享元对象的内部状态时一致的
    private String intrinsicState;

    public Flyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    public void operation(String extrinsicState) {
        // 根据外部状态进行对应的操作
    }
}

样例代码中,体现的是具体享元类创建后,保存到HashMap中,可以重复利用。其中每个享元类的内部成员属性intrinsicState是对象创建时就确定的。而外部状态不一定是必要的,例如围棋中白子、黑子都是具体的享元对象。其中颜色是齐子的内部状态,在棋盘上的坐标是外部状态。初始创建一个白子和一个黑子,保存到享元池中,每次需要落子取出一个对应颜色的棋子,再传入对应的棋盘坐标。

总结

结合其他模式

  • 在享元模式的享元工厂类中通常提供一个静态的工厂方法用于返回享元对象,使用简单工厂模式来生成享元对象。
  • 在一个系统中,通常只有唯一一个享元工厂,因此可以使用单例模式进行享元工厂类的设计。
  • 享元模式可以结合组合模式形成复合享元模式,统一对多个享元对象设置外部状态。

优点

  • 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
  • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

  • 享元模式需要分离出内部状态和外部状态,从而使得系统变得复杂,这使得程序的逻辑复杂化。
  • 为了使对象可以共享,享元模式需要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

参考资料

[1] 刘伟. 设计模式的艺术[M]. 清华大学出版社, 2020.