> 技术文档 > 设计模式 八:原型模式 (Prototype Pattern)

设计模式 八:原型模式 (Prototype Pattern)


动机(Motivation)

1、在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
2、如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?

解决方法:使用原型模式

原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过构造函数创建。 

基本概念

原型模式的核心思想是:

  • 创建一个原型接口,声明克隆方法

  • 具体类实现这个接口并提供克隆自身的能力

  • 客户端通过请求原型对象克隆自身来创建新对象

实现方式

1. 基本实现

#include #include // 原型基类class Prototype {public: virtual ~Prototype() = default; virtual std::unique_ptr clone() const = 0; virtual void print() const = 0;};// 具体原型类class ConcretePrototype : public Prototype {public: ConcretePrototype(int value) : value_(value) {} // 复制构造函数实现克隆 std::unique_ptr clone() const override { return std::make_unique(*this); } void print() const override { std::cout << \"ConcretePrototype with value: \" << value_ << std::endl; } void setValue(int value) { value_ = value; } private: int value_;};int main() { // 创建原型对象 auto prototype = std::make_unique(10); // 克隆对象 auto clone1 = prototype->clone(); auto clone2 = prototype->clone(); // 修改克隆对象 dynamic_cast(clone1.get())->setValue(20); dynamic_cast(clone2.get())->setValue(30); // 输出结果 prototype->print(); // 输出: ConcretePrototype with value: 10 clone1->print(); // 输出: ConcretePrototype with value: 20 clone2->print(); // 输出: ConcretePrototype with value: 30 return 0;}

2. 使用原型管理器

#include #include #include class Prototype {public: virtual ~Prototype() = default; virtual std::unique_ptr clone() const = 0; virtual void print() const = 0;};class ConcretePrototypeA : public Prototype {public: ConcretePrototypeA(int value) : value_(value) {} std::unique_ptr clone() const override { return std::make_unique(*this); } void print() const override { std::cout << \"ConcretePrototypeA with value: \" << value_ << std::endl; } private: int value_;};class ConcretePrototypeB : public Prototype {public: ConcretePrototypeB(std::string str) : str_(std::move(str)) {} std::unique_ptr clone() const override { return std::make_unique(*this); } void print() const override { std::cout << \"ConcretePrototypeB with string: \" << str_ << std::endl; } private: std::string str_;};class PrototypeManager {public: void registerPrototype(const std::string& key, std::unique_ptr prototype) { prototypes_[key] = std::move(prototype); } std::unique_ptr create(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> prototypes_;};int main() { PrototypeManager manager; // 注册原型 manager.registerPrototype(\"A\", std::make_unique(100)); manager.registerPrototype(\"B\", std::make_unique(\"Hello\")); // 从原型创建对象 auto obj1 = manager.create(\"A\"); auto obj2 = manager.create(\"B\"); auto obj3 = manager.create(\"A\"); if (obj1) obj1->print(); // 输出: ConcretePrototypeA with value: 100 if (obj2) obj2->print(); // 输出: ConcretePrototypeB with string: Hello if (obj3) obj3->print(); // 输出: ConcretePrototypeA with value: 100 return 0;}

 UML结构

 

原型模式的优点

  1. 减少子类数量:不需要为每种对象创建专门的子类

  2. 动态配置应用:可以在运行时添加或删除原型

  3. 简化对象创建:特别是当对象初始化过程复杂时

  4. 性能优化:克隆通常比新建对象更高效

适用场景

  1. 当系统需要独立于其产品的创建、组合和表示时

  2. 当要实例化的类是在运行时指定时

  3. 当需要避免建立与产品类层次平行的工厂类层次时

  4. 当一个类的实例只能有几个不同状态组合中的一种时

注意事项

  1. 深拷贝与浅拷贝问题:确保克隆操作正确地复制了所有成员变量

  2. 对于包含循环引用的对象,需要特别处理克隆逻辑

  3. 原型模式可能隐藏了对象的创建细节,使代码更难理解

原型模式在C++中特别有用,因为它可以利用拷贝构造函数和赋值操作符来实现克隆操作,同时结合智能指针可以很好地管理内存。