← 返回题目列表

享元模式有哪些角色?调用流程是什么?

高频 简单 第 2 / 25 题 更新于 2026/07/28
享元模式角色调用流程

简化版

享元模式主要包括享元接口、具体享元、享元工厂和客户端。客户端通过享元工厂获取共享对象,再把外部状态传给享元对象的方法执行具体逻辑。

详细版

享元模式常见角色有:

  • Flyweight:享元接口,定义接收外部状态的操作。
  • ConcreteFlyweight:具体享元,保存内部状态。
  • FlyweightFactory:享元工厂,负责缓存和复用享元对象。
  • Client:客户端,维护外部状态,并从工厂获取享元。

调用流程通常是:

  1. 客户端根据内部状态 key 请求享元对象。
  2. 工厂先查缓存,有则返回,没有则创建并缓存。
  3. 客户端调用享元方法时传入外部状态。
  4. 享元对象使用内部状态和外部状态完成工作。

完整版教学

一、Flyweight:享元接口

享元接口定义对象对外提供的行为。它通常会把外部状态作为方法参数。

例如棋子:

interface ChessPiece {
    void display(int x, int y);
}

xy 是外部状态,不保存在棋子对象内部,而是在使用时传入。

二、ConcreteFlyweight:具体享元

具体享元保存可共享的内部状态。

class ConcreteChessPiece implements ChessPiece {
    private final String color;

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

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

这里 color 是内部状态。黑棋对象可以被很多落子位置复用。

三、FlyweightFactory:享元工厂

享元工厂是享元模式的关键角色。它负责管理共享对象池,保证同样内部状态对应的享元对象可以复用。

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

    public ChessPiece getChessPiece(String color) {
        return pool.computeIfAbsent(color, ConcreteChessPiece::new);
    }
}

客户端不应该随意 new 享元对象,否则共享池就失效了。

四、Client:客户端

客户端负责维护外部状态。

例如棋盘需要记录每一步棋的位置:

class ChessBoard {
    private final ChessPieceFactory factory = new ChessPieceFactory();

    public void put(String color, int x, int y) {
        ChessPiece piece = factory.getChessPiece(color);
        piece.display(x, y);
    }
}

棋盘知道坐标,棋子知道颜色。两者职责分开。

五、完整调用链

一次享元调用可以拆成:

  1. 客户端识别需要的内部状态,例如颜色是黑色。
  2. 客户端向工厂请求黑棋享元。
  3. 工厂检查对象池。
  4. 如果对象池已有黑棋,直接返回。
  5. 如果没有,创建黑棋并放入对象池。
  6. 客户端传入坐标等外部状态,调用享元行为。

这条链路中,真正避免重复创建的是享元工厂。

六、角色边界要清楚

享元对象不应该保存外部状态,例如棋子不应该保存坐标。否则同一个棋子对象被多个位置共享时,状态会相互覆盖。

享元工厂也不应该承担过多业务逻辑。它主要负责对象复用和生命周期管理,复杂业务应该留在客户端或应用服务层。

七、常见误区与追问

享元流程从完整键开始:客户端把内部状态键交给 Factory,工厂返回1个共享 ConcreteFlyweight;客户端再把本次外部状态传给业务方法。若系统含100个上下文但只有4个内部状态组合,Factory 理想上只维护4个共享实例。UnsharedConcreteFlyweight 则用于确实不能共享的特殊对象。

检查维度判定依据
FlyweightFactory按键查找、创建并管理共享对象
Context保存或计算外部状态并调用享元
Context(E) -> Factory(key I) -> Flyweight(I) -> operation(E)

记忆钩子:工厂找“共性”,上下文带“个性”,两者在调用时合并。

  • 误区:客户端可以绕过工厂随意 new 共享对象。 这会破坏按键复用,通常应集中由工厂创建。
  • 追问:UnsharedConcreteFlyweight 是否必须? 不是,它只是经典结构中为不可共享情况保留的可选角色。
  • 误区:Factory 负责保存外部状态。 外部状态随使用变化,应由 Context 或调用者管理。
  • 追问:共享对象如何安全发布? 构造完成后通过并发安全映射发布,并保持内部状态不可变。
  • 追问:流程测试检查什么? 检查命中复用、不同键隔离,以及外部状态不会写入共享实例。

八、加强记忆

享元模式的角色可以记成“工厂管共享,对象管内部,客户端管外部”:工厂负责复用享元,享元保存可共享状态,客户端在调用时传入变化状态。