# 享元模式(Flyweight Pattern)

目录: https://note.dolyw.com/design/ (opens new window)

代码地址

# 1. 介绍

享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式,重用现有的同类对象,如果未找到匹配的对象,则创建新对象

享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能,其实就是 JDK 中的字符串常量池,数据库连接池的应用,池化思想

# 1.1. 特点

  • 运用共享技术来有效地支持大量细粒度对象的复用
  • 通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率
  • 相同对象只要保存一份,降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力

# 1.2. 结构

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

# 1.3. 优缺点

  • 意图:运用共享技术有效地支持大量细粒度的对象
  • 主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建
  • 何时使用:
    • 系统中有大量对象
    • 这些对象消耗大量内存
    • 这些对象的状态大部分可以外部化
    • 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替
    • 系统不依赖于这些对象身份,这些对象是不可分辨的
  • 如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象
  • 关键代码:用 HashMap 存储这些对象
  • 优点:大大减少对象的创建,降低系统的内存,使效率提高。
  • 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱
  • 使用场景:
    • 系统有大量相似对象
    • 需要缓冲池的场景
    • JDK 中的字符串常量池,数据库连接池
  • 注意事项:
    • 注意划分外部状态和内部状态,否则可能会引起线程安全问题
    • 这些类必须有一个工厂对象加以控制

# 1.4. 举例

  • JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面
  • 数据库的数据池
  • HashMap 存储对象

# 2. 代码

HashMap 存储对象的例子,不考虑线程安全先

# 2.1. 抽象享元

/**
 * 抽象享元
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/1/24 14:12
 */
public interface Flyweight {

    /**
     * 处理方法
     *
     * @param unsharableFlyweight
     * @return void
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2022/1/24 14:15
     */
    void handle(UnsharableFlyweight unsharableFlyweight);

}

# 2.2. 具体享元

/**
 * 具体享元
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/1/24 14:12
 */
public class ConcreteFlyweight implements Flyweight {

    private String key;

    public ConcreteFlyweight(String key) {
        this.key = key;
    }

    @Override
    public void handle(UnsharableFlyweight unsharableFlyweight) {
        System.out.println(this.key + "被调用,非享元信息:" + unsharableFlyweight.getMsg());
    }
}

# 2.3. 非享元角色

/**
 * 非享元角色
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/1/24 14:14
 */
public class UnsharableFlyweight {

    private String msg;

    public UnsharableFlyweight(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}

# 2.4. 享元工厂

/**
 * 享元工厂
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/1/24 14:15
 */
public class FlyweightFactory {

    private Map<String, Flyweight> flyweightMap = new HashMap<>(16);

    /**
     * 工厂根据key获取Flyweight
     *
     * @param key
     * @return com.design.flyweight.Flyweight
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2022/1/24 14:19
     */
    public Flyweight getFlyweight(String key) {
        Flyweight flyweight = flyweightMap.get(key);
        if (flyweight == null) {
            flyweight  = new ConcreteFlyweight(key);
            flyweightMap.put(key, flyweight);
            System.out.println(key + "不存在,进行创建");
        } else {
            System.out.println(key + "存在,成功获取");
        }
        return flyweight;
    }

}

# 2.5. 执行

/**
 * 享元模式
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/1/24 13:44
 */
public class Main {

    public static void main(String[] args) {
        // 创建工厂
        FlyweightFactory flyweightFactory = new FlyweightFactory();
        // 使用工厂创建3个apple,2个banana对象
        Flyweight apple1 = flyweightFactory.getFlyweight("apple");
        Flyweight apple2 = flyweightFactory.getFlyweight("apple");
        Flyweight apple3 = flyweightFactory.getFlyweight("apple");
        Flyweight banana1 = flyweightFactory.getFlyweight("banana");
        Flyweight banana2 = flyweightFactory.getFlyweight("banana");
        // 使用不同的对象处理发现使用的其实是同一个
        apple1.handle(new UnsharableFlyweight("第1次处理apple"));
        apple2.handle(new UnsharableFlyweight("第2次处理apple"));
        apple3.handle(new UnsharableFlyweight("第3次处理apple"));
        banana1.handle(new UnsharableFlyweight("第1次处理banana"));
        banana2.handle(new UnsharableFlyweight("第2次处理banana"));
    }

}

输出

apple不存在,进行创建
apple存在,成功获取
apple存在,成功获取
banana不存在,进行创建
banana存在,成功获取
apple被调用,非享元信息:1次处理apple
apple被调用,非享元信息:2次处理apple
apple被调用,非享元信息:3次处理apple
banana被调用,非享元信息:1次处理banana
banana被调用,非享元信息:2次处理banana
上次更新时间: 2023-12-15 03:14:55