设计模式(五)原型模式 — 通过克隆快速复制对象,避免复杂初始化

在复杂对象创建成本高、初始化逻辑繁琐或需要大量重复对象的场景中,原型模式(Prototype Pattern)提供了一条极具效率的捷径:通过"克隆"已有实例来创建新对象。它避开构造过程,实现结构与数据的快速复制,是许多高性能框架与中间件的常用武器。


🧬 一、原型模式的核心思想

原型模式的本质是:使用原型实例指定要创建对象的类型,并通过复制这个原型创建新的对象

当对象的创建代价很高(例如依赖复杂计算、网络请求、数据库查询),或当类的结构允许多个相似但稍有不同的实例时,克隆可以比构造函数更高效、更直接。


🏗️ 二、为什么不直接 new?构造开销的真实成本

在实际工程中,"创建一个对象"有时并非简单的 new

  • 涉及深度嵌套的对象树初始化
  • 需要大量参数配置
  • 需要从外部系统获取数据完成初始化
  • 需要执行计算成本高的预处理过程
  • 涉及昂贵的依赖注入或框架操作

原型模式通过跳过初始化流程,直接复制现有对象,帮助我们节省大量时间与资源。


🧱 三、原型模式的结构概览

uses Prototype +clone() : Prototype ConcretePrototypeA -field1 -field2 +clone() : ConcretePrototypeA Client +operation()

关键角色包括:

  • Prototype:定义 clone() 方
  • ConcretePrototype:实现克隆逻辑
  • Client:通过原型对象执行复制,不依赖具体类型

✂️ 四、Java 中的原型实现方式

Java 原型模式的基础来自 Cloneable 接口与 Object.clone() 方法:

  • Cloneable 是一个"标记接口"(无方法)
  • Object.clone() 是受保护方法,需要手动 override

示例代码

java 复制代码
public class User implements Cloneable {
    private String name;
    private Address address; // 引用类型

    public User(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    protected User clone() {
        try {
            return (User) super.clone(); // 浅拷贝
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

这种方式是"浅拷贝",对复杂对象要特别注意。


🔍 五、浅拷贝 vs 深拷贝

浅拷贝 :复制对象,但内部引用类型共享同一实例
深拷贝:复制对象及其内部所有引用类型

例如:
原对象 Address 实例 浅拷贝对象 深拷贝对象 复制的 Address 实例

浅拷贝共享 Address,深拷贝拥有独立 Address。

进行深拷贝的常见方式:

  • 手动 new 内部对象
  • 通过序列化(但性能一般)
  • 使用 JSON 转换(简洁但略慢)
  • 使用第三方库(如 Kryo)

🗃️ 六、构建深拷贝示例(Java)

java 复制代码
@Override
protected User clone() {
    try {
        User cloned = (User) super.clone();
        cloned.address = this.address.clone(); // 深拷贝内部引用
        return cloned;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

同时 Address 类也需要实现 Cloneable


⚙️ 七、原型登记表:更灵活的原型管理方式

在大型系统中,可能存在多个原型对象。此时可以维护一个"原型池"(Prototype Registry),通过 key 获取原型并克隆。

java 复制代码
public class PrototypeRegistry {
    private static final Map<String, Prototype> map = new HashMap<>();

    public static void register(String key, Prototype prototype) {
        map.put(key, prototype);
    }

    public static Prototype get(String key) {
        return map.get(key).clone();
    }
}

优点:

  • 类似工厂模式,但避免复杂构造
  • 动态注册与替换原型
  • 适合运行时配置

🧩 八、原型模式在实际工程中的典型应用场景

1. 复杂对象初始化

如:地图 tile、三维模型、界面组件树。

2. 动态配置克隆

如:报表模板、规则模板、工作流节点模板。

3. 游戏开发

如:怪物、道具、武器模板。

4. 大数据与缓存

如:创建高开销数据模型副本、数据快照。

5. 框架层面

Spring、Netty 等框架都有内部原型对象缓存机制。


🚀 九、使用原型模式实现运行时动态扩展

原型可以帮助系统在运行时注入新的对象模板,而无需修改代码。例如:

  • 根据用户配置生成不同 UI 样式对象
  • 为不同客户生成特化的业务流程
  • 根据运行监控实时调整对象行为(如 ML 模型缓存)
  • 这种灵活性是静态类结构与构造器难以实现的。

🧪 十、与工厂模式的对比

对比项 工厂模式 原型模式
生成方式 调用构造函数 克隆现有对象
初始化成本 通常较高 最小
扩展性 修改工厂 注册新原型即可
使用场景 单对象生命周期明确 大量相似对象需快速复制

🧱 十一、与建造者模式的差异

建造者模式适合复杂且有步骤的构建流程

原型模式适合已经有模板实例、希望快速复制的情况。

如果你已经构造过一个对象,并希望再来几十个近似副本,显然克隆更有效。


🧲 十二、原型模式的优点与缺点

优点

  • 大幅减少对象创建成本
  • 动态添加新原型,无需修改现有代码
  • 提供深度复制灵活性
  • 简化复杂对象的重复构建

缺点

  • 深拷贝实现复杂
  • Java 的 clone 机制较为古老且容易出错
  • 原型链中的对象需实现一致的 clone 协议

🛠️ 十三、现代 Java 实践建议

由于 Java 的 Cloneable 机制设计不够优雅,业界常用更现代的方式替代:

  • 复制构造器(Copy Constructor)

    java 复制代码
    public User(User other) {
        this.name = other.name;
        this.address = new Address(other.address);
    }

静态工厂:from()

对象序列化或 JSON 克隆(适合数据模型)

尽管如此,原型模式的思想依然十分重要------尤其在对性能敏感的场景中。


🧭 十四、结语

原型模式通过"以物易物"的方式,让对象生成不再受构造过程束缚,在性能优化、动态配置及复杂对象复制方面都非常高效。掌握它不仅能写出更灵活的系统,也能在关键时刻成为性能优化的利器。

相关推荐
敖云岚16 小时前
【设计模式】简单易懂的行为型设计模式-策略模式
设计模式·策略模式
IT永勇1 天前
C++设计模式-单例
c++·单例模式·设计模式
ZHE|张恒1 天前
设计模式(四)建造者模式 — 分步骤构建复杂对象,让创建过程可控可扩展
设计模式·建造者模式
ZHE|张恒1 天前
设计模式(三)抽象工厂模式 — 一次性创建一整套相关对象的终极工厂
设计模式·抽象工厂模式
崎岖Qiu1 天前
状态模式与策略模式的快速区分与应用
笔记·设计模式·状态模式·策略模式·开闭原则
明洞日记2 天前
【设计模式手册007】原型模式 - 通过复制创建对象的艺术
java·设计模式·原型模式
Jonathan Star2 天前
JavaScript 中,原型链的**最顶端(终极原型)只有一个——`Object.prototype`
开发语言·javascript·原型模式
u***j3242 天前
算法设计模式总结
算法·设计模式
烤麻辣烫2 天前
23种设计模式(新手)-7迪米特原则 合成复用原则
java·开发语言·学习·设计模式·intellij-idea