简单来说,原型模式(Prototype Pattern) 的核心思想就是:"与其从零创建一个新对象,不如直接克隆一个现有的对象。"
想象一下,如果你需要给 10 个不同的部门提供你的身份证副本,有两种方法:
-
非原型模式(手动重绘): 你拿出一张白纸,对着身份证,一笔一划地把头像、文字、国徽全部画出来。每办一个业务,你就得重新画一遍。
-
原型模式(复印机): 你把身份证(原型)放在复印机上,按下"复印"键(
clone)。复印机直接扫描物理特征并产生副本。
1. 不使用原型模式:手动重绘
在这种模式下,客户端(打印店员工)必须知道身份证的所有细节(姓名、号码等),并且每需要一个副本,都要重新调用构造函数并手动设置属性。
#include <iostream>
#include <string>
class IDCard {
public:
std::string name;
std::string idNumber;
std::string purpose;
// 构造函数:模拟"办证"过程,非常麻烦
IDCard(std::string n, std::string id) : name(n), idNumber(id) {
std::cout << "[系统] 正在进行繁琐的身份核验、制卡..." << std::endl;
}
};
// 客户端代码
int main() {
// 假设我们已经有一个原件了
IDCard myID("张三", "110101...");
// 需求:现在需要两个副本,分别给银行和健身房
// 缺点:你必须手动重复输入所有信息,万一 idNumber 输错一位就麻烦了
IDCard copy1(myID.name, myID.idNumber);
copy1.purpose = "银行开户";
IDCard copy2(myID.name, myID.idNumber);
copy2.purpose = "健身房会员";
return 0;
}
2. 使用原型模式:复印机一键克隆
在这种模式下,身份证类自带一个 clone() 方法。客户端不需要关心身份证里到底有什么,只需要说"给我复印一份"即可。
#include <iostream>
#include <string>
#include <memory>
// 抽象原型
class Prototype {
public:
virtual ~Prototype() {}
virtual std::unique_ptr<Prototype> clone() const = 0;
virtual void setPurpose(std::string p) = 0;
virtual void display() const = 0;
};
// 具体原型:身份证
class IDCard : public Prototype {
private:
std::string name;
std::string idNumber;
std::string purpose;
public:
IDCard(std::string n, std::string id) : name(n), idNumber(id), purpose("原件") {
std::cout << "[系统] 正在进行繁琐的身份核验、制卡..." << std::endl;
}
// 关键:克隆方法(利用拷贝构造函数)
std::unique_ptr<Prototype> clone() const override {
return std::make_unique<IDCard>(*this);
}
void setPurpose(std::string p) override { purpose = p; }
void display() const override {
std::cout << "姓名:" << name << " | 用途:" << purpose << std::endl;
}
};
// 客户端代码
int main() {
// 1. 只有一份原件
std::unique_ptr<Prototype> myID = std::make_unique<IDCard>("张三", "110101...");
// 2. 需求:需要副本
// 优点:无需再次输入姓名和号码,直接克隆,既快又准
auto copy1 = myID->clone();
copy1->setPurpose("银行开户");
auto copy2 = myID->clone();
copy2->setPurpose("健身房会员");
copy1->display();
copy2->display();
return 0;
}
3. 直观对比总结
| 特性 | 不使用原型模式 | 使用原型模式 |
|---|---|---|
| 创建方式 | 必须 new 一个具体类。 |
调用已存在对象的 clone()。 |
| 依赖程度 | 高。 必须知道对象的构造函数参数。 | 低。 只需要知道 clone() 接口。 |
| 复杂性 | 每次创建都要手动重新赋值(容易出错)。 | 状态自动复制,只需修改差异部分。 |
| 性能开销 | 大。 每次都要跑一遍复杂的构造逻辑。 | 小。 通常是高效的内存拷贝。 |
| 适用场景 | 对象很简单,属性很少。 | 对象初始化极慢,或者属性极其复杂。 |