← 返回题目列表

原型模式有哪些角色?调用流程是什么?

高频 简单 第 2 / 25 题 更新于 2026/07/28
原型模式PrototypeConcretePrototypeClient调用流程

简化版

原型模式通常有三个角色:Prototype 定义复制接口,ConcretePrototype 实现具体复制逻辑,Client 通过原型对象创建新对象。调用流程是客户端拿到一个原型对象,调用复制方法得到新对象,再按需修改差异字段。

详细版

原型模式的典型角色:

角色作用
Prototype抽象原型,定义 clone()copy()
ConcretePrototype具体原型,实现复制逻辑
Client使用原型复制新对象
PrototypeRegistry可选角色,集中管理多个原型对象

典型流程:

Client 获取原型对象
Client 调用 prototype.copy()
ConcretePrototype 创建并返回副本
Client 修改副本的差异字段

如果系统有多种原型,可以使用注册表:

registry.get("vip-user").copy()
registry.get("normal-user").copy()

原型模式让客户端不必关心复杂对象如何初始化,只需要复制合适的模板。

完整版教学

一、Prototype:定义复制能力

Prototype 是抽象接口。

interface Prototype<T> {
    T copy();
}

copy() 比直接暴露 Java 的 clone() 更清晰,因为 clone() 在 Java 里有一些历史包袱,例如访问权限、Cloneable 标记接口和异常处理。

二、ConcretePrototype:实现复制逻辑

具体原型类负责创建自身副本。

class DocumentTemplate implements Prototype<DocumentTemplate> {
    private String title;
    private List<String> sections;

    public DocumentTemplate copy() {
        DocumentTemplate copy = new DocumentTemplate();
        copy.title = this.title;
        copy.sections = new ArrayList<>(this.sections);
        return copy;
    }
}

复制逻辑由对象自己掌握,因为对象最清楚哪些字段要复制、哪些字段要重新生成、哪些字段可以共享。

三、Client:使用原型但不关心构造细节

客户端只使用复制结果:

DocumentTemplate base = registry.get("tech-doc");
DocumentTemplate doc = base.copy();
doc.setTitle("原型模式面试题");

客户端不需要知道默认章节、样式、权限等字段如何初始化。

这能减少重复初始化代码,也能隐藏复杂构造细节。

四、PrototypeRegistry:集中管理原型

如果只有一个原型对象,客户端直接持有即可。

如果原型很多,可以使用注册表:

class PrototypeRegistry {
    private final Map<String, Prototype<?>> prototypes = new HashMap<>();

    void register(String key, Prototype<?> prototype) {
        prototypes.put(key, prototype);
    }

    Prototype<?> get(String key) {
        return prototypes.get(key);
    }
}

注册表让客户端通过名称获取模板,而不是到处创建原型对象。

五、调用流程中的关键边界

复制时要明确三件事:

  • 副本是否是新对象;
  • 可变引用是否需要深拷贝;
  • ID、创建时间、状态等字段是否应该重新生成。

例如复制订单模板时,商品配置可以复制,但订单号不能复制。

这类字段如果不处理,会产生严重业务问题。

六、常见误区与追问

角色流程可用注册表场景验证:先登记2个配置完整的原型,客户端按 key 取出时得到副本,而不是内部模板本身。ConcretePrototype 最了解自身字段,应承担复制规则;Registry 只负责选择和保存,不应偷偷决定每个字段的深浅复制。

检查维度判定依据
Prototype声明复制契约
Registry按键选择模板并返回副本

记忆钩子:原型负责“怎么复制”,注册表负责“复制哪个”。

  • 误区:原型模式必须有注册表。 注册表是常见配套角色,不是模式成立的必要条件。
  • 追问:谁负责初始化原型? 启动配置、工厂或管理服务都可以,关键是初始化与复制职责分离。
  • 误区:Client 应拿到原型后自行补齐深拷贝。 这会让复制规则散落,具体原型应封装完整契约。
  • 追问:注册表并发读取安全吗? 模板不可变且映射安全发布时读取较简单,动态更新则需并发控制。
  • 追问:如何防止原型被污染? 注册时保存受控模板,获取时只返回副本,并限制外部直接修改内部对象。

七、加强记忆

原型模式的角色可以记成“接口定义复制,具体类负责复制,客户端拿副本使用”。如果原型很多,再加一个注册表统一管理。流程简单,但复制边界要谨慎,尤其是可变引用、唯一标识和业务状态字段。