「全网最细 + 实战源码案例」设计模式——享元模式

核心思想

  • 享元模式(Flyweight Pattern)是一种结构型设计模式,主要用于减少程序中大量对象的内存消耗。该模式通过共享相同的数据来有效减少内存的使用,适用于对象非常多且可以共享一部分状态的场景。
  • 核心:将对象的内部状态外部状态分离
    • 内部状态:存储在享元对象内部的、不会改变的状态,通常是可以共享的。
    • 外部状态:依赖于环境且变化的状态,不可共享。

结构

1. Flyweight(享元角色)

  • 声明享元对象的接口,通常是不可变的。

2. ConcretFlyweight(具体享元角色)

  • 实现 Flyweight 接口,存储共享状态。

3. UnsharedConcreteFlyweight(非共享具体享元角色)

  • 非共享的享元对象,一般不是由享元工厂创建。

4. FlyweightFactory(享元工厂)

  • 用来管理享元对象的工厂类,确保共享对象的唯一性。

适用场景

  1. 存在大量相似对象
  2. 对象状态可分为内部、外部状态
  3. 节省内存,避免重复创建对象。

优缺点

优点:

  1. 节省内存:避免重复创建相似对象。
  2. 提高性能:减少重复创建对象的开销。

缺点:

  1. 增加复杂性:享元模式、工厂类的设计。

实现步骤

  1. 将需要改写为享元的类成员变量拆分为两个部分:
    1. 内在状态: 包含不变的、 可在许多对象中重复使用的数据的成员变量。
    2. 外在状态: 包含每个对象各自不同的情景数据的成员变量
  1. 保留类中表示内在状态的成员变量, 并将其属性设置为不可修改。 这些变量仅可在构造函数中获得初始数值。
  2. 找到所有使用外在状态成员变量的方法, 为在方法中所用的每个成员变量新建一个参数, 并使用该参数代替成员变量。
  3. 你可以有选择地创建工厂类来管理享元缓存池, 它负责在新建享元时检查已有的享元。 如果选择使用工厂, 客户端就只能通过工厂来请求享元, 它们需要将享元的内在状态作为参数传递给工厂。
  4. 客户端必须存储和计算外在状态 (情景) 的数值, 因为只有这样才能调用享元对象的方法。 为了使用方便, 外在状态和引用享元的成员变量可以移动到单独的情景类中。

示例

复制代码
// 享元角色
public abstract class AbstractBox {

    // 获取图形
    public abstract String getShape();

    // 显示图形及颜色
    public void display(String color){
        System.out.println("方块:" + getShape() + ",颜色:" + color);
    }
}

// 具体享元角色------I
public class IBox extends AbstractBox{
    @Override
    public String getShape() {
        return "I";
    }
}

// 具体享元角色------L
public class LBox extends AbstractBox{
    @Override
    public String getShape() {
        return "L";
    }
}

// 具体享元角色------O
public class OBox extends AbstractBox{
    @Override
    public String getShape() {
        return "O";
    }
}

// 享元工厂(静态内部类单例方式)
public class BoxFactory {

    // 享元对象池
    private Map<String, AbstractBox> boxes;

    // 私有化构造器
    private BoxFactory() {
        boxes = new HashMap<>();
        boxes.put("L", new LBox());
        boxes.put("I", new IBox());
        boxes.put("O", new OBox());
    }

    // 获取享元对象
    public AbstractBox getBox(String type) {
        return boxes.get(type);
    }

    // 创建静态内部类
    private static class SingletonHolder {
        private static final BoxFactory INSTANCE = new BoxFactory();
    }

    // 获取享元工厂实例
    public static BoxFactory getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {

        // 1.获取享元工厂
        BoxFactory boxFactory = BoxFactory.getInstance();

        // 2.获取享元对象
        AbstractBox box1 = boxFactory.getBox("L");
        AbstractBox box2 = boxFactory.getBox("I");
        AbstractBox box3 = boxFactory.getBox("O");
        AbstractBox box4 = boxFactory.getBox("O");

        // 3.获取非享元对象并显示
        box1.display("red");
        box2.display("blue");
        box3.display("green");
        box4.display("yellow");

        // 4.验证享元对象是否共享
        System.out.println(box3 == box4);

    }
}

在源码中的应用


与其他模式的关系

  • 你可以使用享元模式实现组合模式树的共享叶节点以节省内存。
  • 享元展示了如何生成大量的小型对象, 外观模式则展示了如何用一个对象来代表整个子系统。
  • 如果你能将对象的所有共享状态简化为一个享元对象, 那么享元就和单例模式类似了。 但这两个模式有两个根本性的不同。
    • 只会有一个单例实体, 但是享元类可以有多个实体, 各实体的内在状态也可以不同。
    • 单例对象可以是可变的。 享元对象是不可变的。
相关推荐
李姆斯14 小时前
复盘上瘾症:到底什么时候该“复盘”,什么时候不需要“复盘”
前端·后端·团队管理
javachen__14 小时前
Spring Boot配置error日志发送至企业微信
spring boot·后端·企业微信
seabirdssss14 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续14 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben04414 小时前
ReAct模式解读
java·ai
烛阴15 小时前
【TS 设计模式完全指南】从“入门”到“劝退”,彻底搞懂单例模式
javascript·设计模式·typescript
轮到我狗叫了15 小时前
牛客.小红的子串牛客.kotori和抽卡牛客.循环汉诺塔牛客.ruby和薯条
java·开发语言·算法
OC溥哥99916 小时前
Flask论坛与个人中心页面开发教程完整详细版
后端·python·flask·html
Volunteer Technology16 小时前
三高项目-缓存设计
java·spring·缓存·高并发·高可用·高数据量
栗子~~16 小时前
bat脚本- 将jar 包批量安装到 Maven 本地仓库
java·maven·jar