原型模式:克隆对象而非新建

FreeGuideOnline 最新 2026-06-18

原型模式详解:克隆对象而非新建

什么是原型模式?

原型模式(Prototype Pattern)是一种创建型设计模式,它允许你通过复制现有对象来创建新对象,而无需依赖类的构造函数或实例化过程。核心思想是:用克隆替代新建

在软件开发中,有些对象的创建成本很高,比如涉及到数据库读取、网络请求或复杂的初始化计算。如果直接新建对象,会重复执行这些高开销操作。原型模式让你可以“拷贝”一个对象,只需要付出一次昂贵的创建代价,之后就能快速复制。

现实类比

想象你要批量制作请柬正文一样的手写邀请函。与其每一封都从头书写一遍,你更愿意先精心写好一份模板(原型),然后使用复印机快速复制。这不仅能保证内容一致,还大大节省了时间。

原型模式的结构

原型模式的参与者很少,主要包括:

  • Prototype(抽象原型类):声明一个克隆自身的接口,通常就是 clone() 方法。
  • ConcretePrototype(具体原型类):实现克隆方法,完成自身的拷贝工作并返回。
  • Client(客户端):获取一个原型对象,调用其克隆方法创建新对象,而不是直接使用 new

在 Java 中,Cloneable 接口就是一个天然的抽象原型角色,而 Object.clone() 则提供了默认实现。

何时使用原型模式?

  • 对象创建成本大、时间长:例如需要大量参数配置、依赖外部资源、或者需要密集计算的对象。
  • 希望避免复杂的初始化逻辑:通过克隆一个已经配置好的实例,直接获得需要的状态。
  • 系统应该独立于产品的创建、构成和表示方式:你不想让客户端代码耦合具体的类名。
  • 动态生成大量相似对象:比如游戏中的敌人,从一个核心原型克隆不同配置的个体,比反复初始化简单得多。

原型模式的实现要点

1. 实现克隆方法

原型模式的本质是克隆。不同语言的实现差异很大:

  • Java:实现 Cloneable 接口并覆盖 Object.clone(),通常使用浅克隆,需要深克隆时要手动递归克隆引用对象。
  • C#:可以使用 MemberwiseClone() 方法进行浅拷贝,或实现 ICloneable 接口。
  • Python:利用 copy 模块的 copy()(浅拷贝)和 deepcopy()(深拷贝)。
  • JavaScript:可以使用 Object.create() 或展开运算符进行浅复制,深复制则需要借助 structuredClone() 或 Lodash 等工具。

2. 浅克隆 vs 深克隆

这是面试和实际使用中最容易出问题的地方。

  • 浅克隆(Shallow Clone):只复制对象本身,对象内部引用的其它对象和原始对象共享同一份。修改克隆后的引用对象,会影响到原始对象。
  • 深克隆(Deep Clone):不仅复制对象本身,还递归复制其内部引用的所有对象,两个对象完全独立。
浅克隆示意图:
原始对象 A → 引用对象 B
克隆对象 A' → 引用对象 B  (同一个B,没有复制)

深克隆示意图:
原始对象 A → 引用对象 B
克隆对象 A' → 引用对象 B' (B'是B的全新副本)

何时用深克隆:当原型对象内包含可变对象(如列表、自定义对象)且希望克隆体完全独立时,必须实现深克隆。否则会产生非常隐蔽的bug。

3. 原型注册表

在实际项目中,往往结合一个原型管理器(Prototype Registry) 使用。这是一个存储预生成原型的容器,按“键”提供克隆服务,避免每次从零构造原型。

// 伪代码示例:原型注册表
class PrototypeRegistry {
    private Map<String, Shape> prototypes = new HashMap<>();

    public void addPrototype(String key, Shape shape) {
        prototypes.put(key, shape);
    }

    public Shape getClone(String key) {
        Shape prototype = prototypes.get(key);
        return prototype != null ? prototype.clone() : null;
    }
}

实战示例(Java)

定义抽象原型接口

public interface Shape extends Cloneable {
    Shape clone();
    void draw();
}

实现具体原型

public class Circle implements Shape {
    private int radius;
    private Color color; // 颜色对象,用于演示深克隆

    public Circle(int radius, Color color) {
        this.radius = radius;
        this.color = color;
    }

    // 浅克隆:仅复制基本类型和引用
    @Override
    public Circle clone() {
        try {
            return (Circle) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }

    // 深克隆:复制每一个可变引用对象
    public Circle deepClone() {
        return new Circle(this.radius, new Color(this.color.getRGB()));
    }

    @Override
    public void draw() {
        System.out.println("Circle [radius=" + radius + ", color=" + color + "]");
    }
}

客户端使用

public class Client {
    public static void main(String[] args) {
        Circle original = new Circle(5, Color.RED);

        Circle shallowCopy = original.clone();
        Circle deepCopy = original.deepClone();

        // 修改颜色
        original.getColor().darker(); // 假设方法会改变颜色值

        System.out.println("Original: " + original.getColor());
        System.out.println("Shallow: " + shallowCopy.getColor()); // 受影响
        System.out.println("Deep: " + deepCopy.getColor());       // 不受影响
    }
}

原型模式的优缺点

优点

  • 性能优异:避免了耗时的构建过程,尤其当对象创建涉及大量消耗时。
  • 简化构建过程:无需复杂的构造函数、工厂方法,直接克隆一个预配好的实例。
  • 运行时动态配置:可以在运行时通过克隆来改变对象状态,而无需固化到编译期类。
  • 隐藏创建细节:客户端无需知道具体类的名称,只需与原型交互。

缺点

  • 克隆方法实现复杂:尤其是深克隆,需要正确处理循环引用、不可复制资源等问题。
  • 可能破坏单例、不可变对象:如果未正确控制克隆,会出现多个“相同”的实例。
  • 多层继承时麻烦:每个子类都必须实现克隆方法,且要兼顾父类状态。
  • 与工厂模式搭配使用较好,单独使用原型模式往往需要配合注册表。

原型模式 VS 工厂模式

很多初学者分不清何时用原型,何时用工厂。一个简单区分:

  • 工厂模式:侧重于新建对象,用一个方法封装 new,避免客户端直接接触具体类。
  • 原型模式:侧重于复制已有对象,跳过可能复杂的初始化。

如果对象创建本身就是廉价且简单的,用工厂即可。如果创建昂贵、需要保留初始化状态,原型更合适。在成熟框架中(如Spring),经常结合两者:通过 @Scope("prototype") 并在Bean定义中提供原型实例,Spring就会帮您管理原型克隆。

典型应用场景

  • 编辑器中的复制粘贴:从一个文档模板复制出多个副本。
  • 游戏中的怪物生成:从一个基础原型克隆出各种属性的敌人。
  • Java 里的 Object.clone()Arrays.copyOf() 等API的底层思路。
  • 数据库记录缓存对象:缓存一个通用模型,需要时克隆并填充差异。

总结

原型模式通过“克隆”而不是“新建”来创建对象,是兼顾性能与灵活性的优雅解法。掌握它关键在于理解深/浅克隆的区别,并能根据实际场景设计好原型注册机制。不要在有大量互斥状态或不可变对象无意义克隆的场景强行使用,合理搭配其他创建型模式,才能发挥最大价值。

一句话口诀:新建成本高,克隆来帮忙;浅复引用通,深复独立强。