1. 概述

GoF分类

原型模式属于创建型设计模式

官方定义

使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

核心目的

通过克隆已有对象来创建新对象,避免直接使用构造函数带来的性能开销和复杂性

解决的问题

  1. 需要创建的对象类型由原型实例决定

  2. 避免创建者与具体产品类的紧耦合

  3. 当直接创建对象的成本较高时(如需要复杂初始化过程)

C++典型应用

  • 游戏开发中的敌人/道具复制

  • 文档编辑器中的元素复制

  • 需要高性能对象创建的场景

  • 复杂对象配置的快速复制

关键特征

  • 通过克隆而非构造创建对象

  • 原型管理器可管理一组原型

  • 支持深拷贝和浅拷贝

  • 减少子类数量

2. 核心思想

设计哲学

“不要重新发明轮子” - 通过复制已有对象来创建新对象,而不是每次都从头开始构建

面向对象原则

  • 开闭原则​:无需修改已有代码即可添加新原型

  • 单一职责原则​:将克隆逻辑与业务逻辑分离

  • 依赖倒置原则​:客户端依赖抽象原型而非具体类

C++特殊考虑

  • 必须正确处理拷贝语义(深拷贝 vs 浅拷贝)

  • 考虑异常安全性

  • 使用智能指针管理原型生命周期

  • 虚析构函数保证正确销毁

工作流程

ClientPrototypeConcretePrototypeclone()创建副本返回新对象返回克隆结果ClientPrototypeConcretePrototype

3. 适用场景

应该使用原型模式的场景

  1. 游戏实体复制
// 敌人原型
auto dragonPrototype = std::make_unique<Dragon>();
// 克隆敌人
auto dragon1 = dragonPrototype->clone();
auto dragon2 = dragonPrototype->clone();
  1. 文档对象复制
// 获取选中的图形原型
auto shapeProto = selectedShape->clone();
// 添加到文档
document.addShape(std::move(shapeProto));
  1. 配置模板复制
// 获取默认配置
auto defaultConfig = configRegistry.getPrototype("default");
// 创建用户配置
auto userConfig = defaultConfig->clone();
userConfig->setUserName("Alice");

不适合使用原型模式的场景

  1. 简单对象创建
  • 直接构造比克隆更简单高效

  • 替代方案:直接使用构造函数

  1. 需要唯一实例的场景
  • 原型模式天然适合复制

  • 替代方案:使用单例模式

  1. 对象状态过于简单
  • 克隆开销可能超过构造开销

  • 替代方案:工厂方法或直接构造

4. UML类图

«interface»
Prototype
+clone()
ConcretePrototypeA
-string data
+clone()
+operation() : void
ConcretePrototypeB
-int value
+clone()
+specialOperation() : void
Client
-Prototype* prototype
+operation() : void

5. 结构

模式参与者

Prototype(抽象原型)​

  • 声明克隆自身的接口

  • 通常为抽象基类或接口

  • 在C++中使用纯虚函数

ConcretePrototype(具体原型)​

  • 实现克隆操作

  • 可能包含复杂的状态数据

  • 决定深拷贝或浅拷贝策略

Client(客户端)​

  • 通过请求原型克隆新对象

  • 无需知道具体原型类

  • 管理原型实例的生命周期

协作流程

  1. 客户端通过原型管理器获取原型实例

  2. 客户端调用原型的clone()方法

  3. 具体原型创建自身的精确副本

  4. 客户端使用克隆得到的新对象

  5. 当不再需要时销毁克隆对象

6. 代码示例

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>

// 抽象原型接口
class Prototype {
public:
    virtual ~Prototype() = default;
    virtual std::unique_ptr<Prototype> clone() const = 0;
    virtual void printInfo() const = 0;
};

// 具体原型A
class ConcretePrototypeA : public Prototype {
public:
    ConcretePrototypeA(const std::string& name, int value)
        : name_(name), value_(value) {}

    std::unique_ptr<Prototype> clone() const override {
        // 使用拷贝构造函数实现深拷贝
        return std::make_unique<ConcretePrototypeA>(*this);
    }

    void printInfo() const override {
        std::cout << "ConcretePrototypeA: " << name_ << ", " << value_ << "\n";
    }

    void setName(const std::string& name) { name_ = name; }
    void setValue(int value) { value_ = value; }

private:
    std::string name_;
    int value_;
};

// 具体原型B
class ConcretePrototypeB : public Prototype {
public:
    ConcretePrototypeB(float data, std::unique_ptr<int> ptr)
        : data_(data), ptr_(std::move(ptr)) {}

    // 深拷贝实现
    std::unique_ptr<Prototype> clone() const override {
        auto newPtr = ptr_ ? std::make_unique<int>(*ptr_) : nullptr;
        return std::make_unique<ConcretePrototypeB>(data_, std::move(newPtr));
    }

    void printInfo() const override {
        std::cout << "ConcretePrototypeB: " << data_;
        if (ptr_) {
            std::cout << ", " << *ptr_;
        }
        std::cout << "\n";
    }

private:
    float data_;
    std::unique_ptr<int> ptr_;
};

// 原型管理器
class PrototypeRegistry {
public:
    void addPrototype(const std::string& key, std::unique_ptr<Prototype> proto) {
        prototypes_[key] = std::move(proto);
    }

    std::unique_ptr<Prototype> clone(const std::string& key) {
        if (prototypes_.find(key) != prototypes_.end()) {
            return prototypes_[key]->clone();
        }
        return nullptr;
    }

private:
    std::unordered_map<std::string, std::unique_ptr<Prototype>> prototypes_;
};

int main() {
    // 初始化原型管理器
    PrototypeRegistry registry;
    
    // 注册原型
    registry.addPrototype("A1", std::make_unique<ConcretePrototypeA>("Original", 42));
    registry.addPrototype("B1", std::make_unique<ConcretePrototypeB>(3.14f, std::make_unique<int>(100)));

    // 克隆并使用对象
    auto cloneA = registry.clone("A1");
    if (cloneA) {
        cloneA->printInfo();
        
        // 可以修改克隆后的对象
        if (auto concreteA = dynamic_cast<ConcretePrototypeA*>(cloneA.get())) {
            concreteA->setName("Modified");
            concreteA->setValue(99);
            concreteA->printInfo();
        }
    }

    auto cloneB = registry.clone("B1");
    if (cloneB) {
        cloneB->printInfo();
    }

    // 测试不存在的原型
    auto cloneC = registry.clone("C1");
    if (!cloneC) {
        std::cout << "Prototype C1 not found\n";
    }

    return 0;
}

7. 运行结果

ConcretePrototypeA: Original, 42
ConcretePrototypeA: Modified, 99
ConcretePrototypeB: 3.14, 100
Prototype C1 not found

输出解释:​

  1. 首先克隆并打印了原型A的初始状态

  2. 修改克隆后的A对象并打印新状态

  3. 克隆并打印了原型B的状态

  4. 尝试克隆不存在的原型C时显示错误信息

8. 应用场景

实际项目案例

C++标准库

  • std::function的复制语义类似于原型模式

  • 各种智能指针的拷贝控制

游戏开发

  • Unity引擎的Prefab系统

  • 敌人/NPC的批量生成

  • 武器/道具的实例化

图形编程

  • 图形元素的复制粘贴

  • 复杂3D模型的实例化

  • 粒子系统的配置模板

行业特定应用

嵌入式系统

  • 硬件配置模板的复制

  • 通信协议的实例生成

  • 节省内存的轻量级复制

高性能计算

  • 计算任务的快速复制

  • 避免重复初始化开销

  • 并行任务的快速生成

9. 变体

主要变体实现

基础变体

// 简单克隆接口
class SimplePrototype {
public:
    virtual SimplePrototype* clone() const = 0;
};

模板变体

template <typename T>
class Cloneable {
public:
    virtual std::unique_ptr<T> clone() const = 0;
};

class Widget : public Cloneable<Widget> {
public:
    std::unique_ptr<Widget> clone() const override {
        return std::make_unique<Widget>(*this);
    }
};

多态变体

class AbstractShape {
public:
    virtual std::unique_ptr<AbstractShape> clone() const = 0;
};

class Circle : public AbstractShape {
    std::unique_ptr<AbstractShape> clone() const override {
        return std::make_unique<Circle>(*this);
    }
};

变体对比表格

变体名称优点缺点适用场景
基础变体简单直接需要手动管理内存简单项目
模板变体类型安全模板复杂性需要强类型保证
多态变体灵活扩展运行时开销复杂继承体系

10. 原型模式 vs. 其他模式

与工厂模式的区别

  • 工厂模式​:通过工厂方法创建新对象

  • 原型模式​:通过克隆现有对象创建新对象

  • 选择依据​:当对象创建成本高时选择原型模式

与单例模式的区别

  • 单例模式​:确保只有一个实例

  • 原型模式​:专门用于创建多个相似实例

  • 组合使用​:单例可以作为原型管理器

11. 原型模式的优缺点

优点分析

  1. 性能优势
  • 避免重复初始化开销

  • 特别适合创建成本高的对象

  • 在C++中可避免虚函数调用开销

  1. 灵活性
  • 运行时动态添加/删除原型

  • 客户端与具体类解耦

  • 减少子类数量

缺点分析

  1. 拷贝语义复杂性
  • 深拷贝实现可能复杂

  • 需要正确处理资源管理

  • C++中需要仔细设计拷贝构造函数

  1. 继承体系限制
  • 每个具体原型必须实现clone()

  • 对已有类的修改可能困难

  • 可能违反里氏替换原则

12. 什么时候不要使用原型模式?

反对信号

  1. 对象构造简单
  • 直接构造比克隆更高效

  • 替代方案:使用构造函数或工厂方法

  1. 需要唯一实例
  • 原型模式天然适合复制

  • 替代方案:单例模式

  1. 对象状态过于复杂
  • 深拷贝实现困难

  • 替代方案:序列化/反序列化

反模式警示

  1. 浅拷贝误用
  • 导致对象间共享状态

  • 正确做法:实现真正的深拷贝

  1. 过度设计
  • 简单场景使用复杂模式

  • 正确做法:KISS原则

13. 总结

核心价值

  • 提供高效的对象创建机制

  • 解耦客户端与具体类

  • 在C++中实现资源安全的对象复制

最佳实践

  1. 明确区分深拷贝和浅拷贝

  2. 使用智能指针管理原型生命周期

  3. 考虑使用原型管理器集中管理

  4. 为clone()方法提供异常安全保证

进阶学习

  1. 研究拷贝构造函数的高级用法

  2. 学习C++对象模型与内存管理

  3. 探索原型模式与其他模式的组合

  4. 实践项目:实现一个图形编辑器原型系统

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐