自学教程 学编程从这里开始

享元模式

《享元模式》|设计模式系列第12篇

系列:结构型模式 · 享元模式(Flyweight Pattern)
关键词:对象池、复用实例、内部状态、外部状态、减少内存占用

一、什么是享元模式

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象。

属于结构型设计模式。

简单理解:
当系统需要创建大量相似小对象时,把对象拆分为内部状态和外部状态。内部状态不变,可以全局共享复用;外部状态由客户端传入,不存储在享元对象内部。通过对象池复用已有实例,避免重复创建大量对象,节约内存。
例子:五子棋棋子,棋子颜色是内部状态可共享;棋子坐标是外部状态,每次下棋动态传入。

核心意图
共享细粒度对象,降低内存开销;区分内部状态与外部状态。

适用场景

  1. 系统存在大量重复细粒度对象,内存占用高
  2. 对象大部分属性可以抽离为不变的内部状态
  3. 对象创建成本较高,适合缓存复用
  4. 可以将对象可变部分提取为外部状态,由客户端传入

⚠️ 缺点预警:需要拆分内部/外部状态,增加代码复杂度;为了节省内存,带来额外的查询逻辑开销。

二、UML结构图

角色划分:

  1. Flyweight(抽象享元):声明接口,接收外部状态
  2. ConcreteFlyweight(具体享元):实现抽象享元,存储内部状态,可共享
  3. UnsharedConcreteFlyweight(非共享享元):不需要共享的享元(可选)
  4. FlyweightFactory(享元工厂):维护享元池,负责创建、复用享元实例
  5. Client(客户端):计算/维护外部状态,调用享元对象

三、Java 完整示例

场景:ChessPiece 棋子,color 为内部状态(共享);x,y 坐标为外部状态(客户端传入)。
代码全部使用英文。

1. Flyweight 抽象享元

public abstract class ChessPiece {
    protected String color;

    public abstract void display(int x, int y);
}

2. ConcreteFlyweight 具体享元

public class ConcreteChessPiece extends ChessPiece {
    public ConcreteChessPiece(String color) {
        this.color = color;
    }

    @Override
    public void display(int x, int y) {
        System.out.println("Piece color:" + color + ", position:(" + x + "," + y + ")");
    }
}

3. FlyweightFactory 享元工厂

import java.util.HashMap;
import java.util.Map;

public class ChessPieceFactory {
    private Map<String, ChessPiece> pool = new HashMap<>();

    public ChessPiece getChessPiece(String color) {
        if (!pool.containsKey(color)) {
            pool.put(color, new ConcreteChessPiece(color));
        }
        return pool.get(color);
    }

    public int getPoolSize() {
        return pool.size();
    }
}

4. Client 客户端

public class Client {
    public static void main(String[] args) {
        ChessPieceFactory factory = new ChessPieceFactory();

        ChessPiece black1 = factory.getChessPiece("black");
        black1.display(1, 2);

        ChessPiece black2 = factory.getChessPiece("black");
        black2.display(3, 4);

        ChessPiece white1 = factory.getChessPiece("white");
        white1.display(5, 6);

        System.out.println("Total shared objects in pool: " + factory.getPoolSize());
        System.out.println("black1 == black2 ? " + (black1 == black2));
    }
}

运行输出:

Piece color:black, position:(1,2)
Piece color:black, position:(3,4)
Piece color:white, position:(5,6)
Total shared objects in pool: 2
black1 == black2 ? true

无论创建多少个黑色棋子,内存中只有一份 black 享元对象;坐标 x,y 每次调用由客户端传入,不存放在享元对象。

四、内部状态 vs 外部状态

  • Internal State:不变、不随场景变化,可以共享,保存在享元对象内部。如棋子颜色。
  • External State:可变,由客户端维护,调用时传入,不保存在享元对象。如棋子坐标。

五、优缺点总结

✅ 优点

  1. 大量细粒度对象场景下,显著节约内存
  2. 对象复用,减少对象创建销毁开销

❌ 缺点

  1. 需要分离内外状态,增加系统复杂度
  2. 运行时需要传递外部状态,增加调用开销

六、框架源码应用

  • JDK String 常量池,典型享元思想
  • Integer.valueOf() 缓存池(-128 ~ 127)
  • Spring 线程池、连接池都属于享元思想延伸

七、小结

享元模式核心:拆分内外状态,通过工厂池复用不变对象,可变参数外部传入。
适用判断:大量相似对象,并且可以拆分出不变内部状态。如果对象状态全部是可变的,不适合享元。

标签:

0 条笔记