动机(Motivation)
1、在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
2、如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
解决方法:使用原型模式
原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过构造函数创建。
基本概念
原型模式的核心思想是:
创建一个原型接口,声明克隆方法
具体类实现这个接口并提供克隆自身的能力
客户端通过请求原型对象克隆自身来创建新对象
实现方式
1. 基本实现
#include <iostream>
#include <memory>
// 原型基类
class Prototype {
public:
virtual ~Prototype() = default;
virtual std::unique_ptr<Prototype> clone() const = 0;
virtual void print() const = 0;
};
// 具体原型类
class ConcretePrototype : public Prototype {
public:
ConcretePrototype(int value) : value_(value) {}
// 复制构造函数实现克隆
std::unique_ptr<Prototype> clone() const override {
return std::make_unique<ConcretePrototype>(*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<ConcretePrototype>(10);
// 克隆对象
auto clone1 = prototype->clone();
auto clone2 = prototype->clone();
// 修改克隆对象
dynamic_cast<ConcretePrototype*>(clone1.get())->setValue(20);
dynamic_cast<ConcretePrototype*>(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 <iostream>
#include <memory>
#include <unordered_map>
class Prototype {
public:
virtual ~Prototype() = default;
virtual std::unique_ptr<Prototype> clone() const = 0;
virtual void print() const = 0;
};
class ConcretePrototypeA : public Prototype {
public:
ConcretePrototypeA(int value) : value_(value) {}
std::unique_ptr<Prototype> clone() const override {
return std::make_unique<ConcretePrototypeA>(*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<Prototype> clone() const override {
return std::make_unique<ConcretePrototypeB>(*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> prototype) {
prototypes_[key] = std::move(prototype);
}
std::unique_ptr<Prototype> 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<Prototype>> prototypes_;
};
int main() {
PrototypeManager manager;
// 注册原型
manager.registerPrototype("A", std::make_unique<ConcretePrototypeA>(100));
manager.registerPrototype("B", std::make_unique<ConcretePrototypeB>("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结构
原型模式的优点
减少子类数量:不需要为每种对象创建专门的子类
动态配置应用:可以在运行时添加或删除原型
简化对象创建:特别是当对象初始化过程复杂时
性能优化:克隆通常比新建对象更高效
适用场景
当系统需要独立于其产品的创建、组合和表示时
当要实例化的类是在运行时指定时
当需要避免建立与产品类层次平行的工厂类层次时
当一个类的实例只能有几个不同状态组合中的一种时
注意事项
深拷贝与浅拷贝问题:确保克隆操作正确地复制了所有成员变量
对于包含循环引用的对象,需要特别处理克隆逻辑
原型模式可能隐藏了对象的创建细节,使代码更难理解
原型模式在C++中特别有用,因为它可以利用拷贝构造函数和赋值操作符来实现克隆操作,同时结合智能指针可以很好地管理内存。