【设计模式】【结构型模式】享元模式(Flyweight)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

一、入门

什么是享元模式?

享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用,特别适用于存在大量相似对象的情况。

它的核心思想是将对象的内在状态 (不变的部分)与外在状态(变化的部分)分离,从而减少对象的数量。

为什么要有享元模式?

假设我们正在开发一个在线文档编辑器(类似Google Docs),用户可以在文档中插入大量字符。每个字符可能包含以下属性:

  • 内在状态(不变的部分):字体、字号、颜色。
  • 外在状态(变化的部分):字符的位置(行号、列号)。

没有使用享元模式的实现:

java 复制代码
class Character {
    private char value;
    private String font;
    private int size;
    private String color;
    private int row;
    private int col;

    public Character(char value, String font, int size, String color, int row, int col) {
        this.value = value;
        this.font = font;
        this.size = size;
        this.color = color;
        this.row = row;
        this.col = col;
    }

    public void print() {
        System.out.println("Character: " + value + ", Font: " + font + ", Size: " + size + ", Color: " + color + ", Position: (" + row + ", " + col + ")");
    }
}

存在问题:

  1. 内存占用过高:大量相似对象重复存储相同的内在状态。
  2. 性能下降:频繁创建和销毁对象导致内存分配和垃圾回收开销。
  3. 资源浪费:相同的内在状态被重复存储,导致资源浪费。

怎么实现享元模式?

享元模式有哪些构成

  1. Flyweight(享元接口):定义享元对象的接口,通常包含一个操作外在状态的方法。
  2. ConcreteFlyweight(具体享元):实现享元接口,存储内在状态。
  3. FlyweightFactory(享元工厂):负责创建和管理享元对象,确保共享。
  4. Client(客户端):维护外在状态,并在需要时调用享元对象。

【案例】在线文档编辑器 - 改

Flyweight(享元接口)FontStyle接口。

java 复制代码
interface FontStyle {
    void applyStyle(char value, int row, int col);
}

ConcreteFlyweight(具体享元):具体享元:存储内在状态。

java 复制代码
class ConcreteFontStyle implements FontStyle {
    private String font;
    private int size;
    private String color;

    public ConcreteFontStyle(String font, int size, String color) {
        this.font = font;
        this.size = size;
        this.color = color;
    }

    @Override
    public void applyStyle(char value, int row, int col) {
        System.out.println("Character: " + value + ", Font: " + font + ", Size: " + size + ", Color: " + color + ", Position: (" + row + ", " + col + ")");
    }
}

FlyweightFactory(享元工厂)FlyweightFactory 享元工厂,管理共享的享元对象。

java 复制代码
class FontStyleFactory {
    private static final Map<String, FontStyle> styles = new HashMap<>();

    public static FontStyle getStyle(String font, int size, String color) {
        String key = font + size + color;
        FontStyle style = styles.get(key);
        if (style == null) {
            style = new ConcreteFontStyle(font, size, color);
            styles.put(key, style);
        }
        return style;
    }
}

Client(客户端): Character类,使用享元对象。

java 复制代码
class Character {
    private char value;
    private int row;
    private int col;
    private FontStyle style;

    public Character(char value, int row, int col, FontStyle style) {
        this.value = value;
        this.row = row;
        this.col = col;
        this.style = style;
    }

    public void print() {
        style.applyStyle(value, row, col);
    }
}

测试类FlyweightPatternDemo

java 复制代码
public class FlyweightPatternDemo {
    public static void main(String[] args) {
        FontStyle style1 = FontStyleFactory.getStyle("Arial", 12, "Black");
        FontStyle style2 = FontStyleFactory.getStyle("Arial", 12, "Black");
        FontStyle style3 = FontStyleFactory.getStyle("Times New Roman", 14, "Red");

        Character char1 = new Character('A', 1, 1, style1);
        Character char2 = new Character('B', 1, 2, style2);
        Character char3 = new Character('C', 2, 1, style3);

        char1.print();
        char2.print();
        char3.print();

        System.out.println("Are style1 and style2 the same object? " + (style1 == style2));
    }
}

输出结果

shell 复制代码
Character: A, Font: Arial, Size: 12, Color: Black, Position: (1, 1)
Character: B, Font: Arial, Size: 12, Color: Black, Position: (1, 2)
Character: C, Font: Times New Roman, Size: 14, Color: Red, Position: (2, 1)
Are style1 and style2 the same object? true

二、享元模式在框架源码中的运用

Java 中的 Integer 缓存

Java 对 Integer 类型也使用了享元模式,缓存了常用的整数值(默认范围为 -128 到 127)。

实现原理

  • 当使用 Integer.valueOf(int) 方法创建 Integer 对象时,JVM 会检查值是否在缓存范围内。
  • 如果在范围内,则返回缓存中的对象;否则创建新的对象。
java 复制代码
Integer i1 = Integer.valueOf(127);
Integer i2 = Integer.valueOf(127);
Integer i3 = Integer.valueOf(128);

System.out.println(i1 == i2); // true,i1 和 i2 指向缓存中的同一个对象
System.out.println(i1 == i3); // false,i3 是堆中新创建的对象

享元模式的体现

  • 内在状态:整数值(如 127)。
  • 外在状态:无。
  • 共享机制:通过缓存共享常用的 Integer 对象。

Java 中的 ThreadPoolExecutor

在 Java 的线程池实现中,享元模式的思想被用于复用线程对象。

实现原理

  • 线程池维护一组线程(享元对象),这些线程可以被多个任务复用。
  • 任务(外在状态)被提交到线程池中,由线程池分配线程执行。
java 复制代码
ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        System.out.println("Task executed by " + Thread.currentThread().getName());
    });
}

executor.shutdown();

享元模式的体现

  • 内在状态:线程对象。
  • 外在状态:任务(RunnableCallable)。
  • 共享机制:线程池通过复用线程对象来减少线程创建和销毁的开销。

三、总结

享元模式的优点

  1. 减少内存占用
    • 通过共享内在状态,减少系统中对象的数量,从而降低内存使用。
    • 特别适用于存在大量相似对象的场景。
  2. 提高性能
    • 减少对象的创建和销毁次数,降低内存分配和垃圾回收的开销。
    • 在需要频繁创建和销毁对象的场景中,性能提升尤为明显。
  3. 优化资源管理
    • 集中管理共享对象,避免资源浪费。
    • 便于统一修改和维护共享对象。
  4. 分离关注点
    • 将内在状态和外在状态分离,使系统设计更加清晰。
    • 客户端只需关注外在状态,享元对象负责处理内在状态。

享元模式的缺点

  1. 增加系统复杂性
    • 需要明确区分内在状态和外在状态,增加了设计的复杂性。
    • 客户端需要维护外在状态,并在调用享元对象时传递这些状态。
  2. 线程安全问题
    • 如果享元对象被多个线程共享,可能需要额外的同步机制来保证线程安全。
    • 这会增加代码的复杂性和运行开销。
  3. 不适用于所有场景
    • 如果对象的状态大部分都是变化的,或者对象之间几乎没有共享的状态,享元模式可能不适用。
    • 在这种情况下,强行使用享元模式可能会导致设计过度复杂。

享元模式的适用场景

享元模式特别适用于以下场景:

  1. 大量相似对象
    • 系统中存在大量相似对象,且这些对象的状态可以分离为内在状态和外在状态。
    • 例子:
      • 文本编辑器中的字符对象。
      • 游戏中的树木、石头、敌人等对象。
  2. 内存敏感的应用
    • 在内存受限的环境中(如嵌入式系统、移动设备),需要优化内存使用。
    • 例子:
      • 嵌入式系统中的图形界面库。
      • 移动应用中的资源管理。
  3. 性能敏感的应用
    • 在需要高性能的场景中(如游戏、图形渲染),减少对象的创建和销毁开销。
    • 例子:
      • 游戏引擎中的粒子系统。
      • 图形渲染中的材质和纹理管理。
  4. 资源共享的场景
    • 需要集中管理共享资源,避免资源浪费。
    • 例子:
      • 数据库连接池。
      • 线程池。
  5. 不可变对象的场景
    • 对象的内在状态是不可变的,适合被共享。
    • 例子:
      • Java 中的 String 常量池。
      • Java 中的 Integer 缓存。
相关推荐
yngsqq2 小时前
c# —— StringBuilder 类
java·开发语言
星星点点洲2 小时前
【操作幂等和数据一致性】保障业务在MySQL和COS对象存储的一致
java·mysql
xiaolingting3 小时前
JVM层面的JAVA类和实例(Klass-OOP)
java·jvm·oop·klass·instanceklass·class对象
风口上的猪20153 小时前
thingboard告警信息格式美化
java·服务器·前端
追光少年33224 小时前
迭代器模式
java·迭代器模式
超爱吃士力架5 小时前
MySQL 中的回表是什么?
java·后端·面试
付聪12105 小时前
装饰器模式
设计模式
扣丁梦想家5 小时前
设计模式教程:外观模式(Facade Pattern)
设计模式·外观模式
扣丁梦想家5 小时前
设计模式教程:装饰器模式(Decorator Pattern)
java·前端·装饰器模式
drebander5 小时前
Maven 构建中的安全性与合规性检查
java·maven