1 概念定义
原型模式是一种创建型设计模式,它通过复制现有对象(原型)来创建新对象,而不是通过new关键字实例化。被复制的对象被称为"原型",这个原型是可定制的。
2 核心思想:
使用原型实例指定创建对象的种类
通过拷贝这些原型创建新的对象
隐藏对象创建的复杂性
提供比直接实例化更高效的对象创建方式
3 主要角色:
Prototype(原型接口):声明克隆方法的接口
ConcretePrototype(具体原型):实现克隆方法的具体类
Client(客户端):通过调用原型对象的克隆方法创建新对象
4 应用场景
以文档编辑系统为例:
需要创建各种复杂的文档模板:简历、报告、合同等
创建这些文档需要复杂的初始化过程
用户希望能够快速复制现有文档并进行修改
文档包含复杂的结构:格式、样式、图表等
5 UML

6 c++ 代码实现
cpp
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <memory>
#include <ctime>
using namespace std;
// 前向声明
class DocumentPrototype;
// ========== 元素类:文档中的段落 ==========
class Paragraph {
private:
string text;
string font;
int fontSize;
string alignment;
bool bold;
bool italic;
public:
Paragraph(const string& t = "", const string& f = "Arial", int size = 12,
const string& align = "left", bool b = false, bool i = false)
: text(t), font(f), fontSize(size), alignment(align), bold(b), italic(i) {}
// 拷贝构造函数
Paragraph(const Paragraph& other)
: text(other.text), font(other.font), fontSize(other.fontSize),
alignment(other.alignment), bold(other.bold), italic(other.italic) {
cout << " 段落拷贝: " << text.substr(0, 20) << "..." << endl;
}
void setText(const string& t) { text = t; }
string getText() const { return text; }
void display() const {
cout << " 段落: " << text << endl;
cout << " 格式: " << font << " " << fontSize << "pt "
<< (bold ? "粗体 " : "") << (italic ? "斜体 " : "")
<< "对齐: " << alignment << endl;
}
// 克隆方法
Paragraph* clone() const {
return new Paragraph(*this);
}
};
// ========== 元素类:文档中的图片 ==========
class Image {
private:
string path;
int width;
int height;
string caption;
public:
Image(const string& p = "", int w = 100, int h = 100, const string& cap = "")
: path(p), width(w), height(h), caption(cap) {}
// 拷贝构造函数
Image(const Image& other)
: path(other.path), width(other.width),
height(other.height), caption(other.caption) {
cout << " 图片拷贝: " << path << endl;
}
void setCaption(const string& cap) { caption = cap; }
string getCaption() const { return caption; }
void display() const {
cout << " 图片: " << path << " [" << width << "x" << height << "] "
<< "标题: " << caption << endl;
}
// 克隆方法
Image* clone() const {
return new Image(*this);
}
};
// ========== 原型接口 ==========
class DocumentPrototype {
public:
virtual ~DocumentPrototype() = default;
virtual DocumentPrototype* clone() const = 0;
virtual void display() const = 0;
virtual string getType() const = 0;
virtual void setTitle(const string& title) = 0;
virtual void modifyContent() = 0;
};
// ========== 具体原型:简历文档 ==========
class ResumeDocument : public DocumentPrototype {
private:
string title;
string author;
time_t createTime;
time_t modifyTime;
vector<Paragraph*> sections;
Image* profilePhoto;
// 私有拷贝构造函数用于克隆
ResumeDocument(const ResumeDocument& other)
: title(other.title), author(other.author),
createTime(other.createTime), modifyTime(time(nullptr)) {
cout << "开始复制简历文档..." << endl;
// 深拷贝段落
for (const auto& para : other.sections) {
sections.push_back(para->clone());
}
// 深拷贝图片
if (other.profilePhoto) {
profilePhoto = other.profilePhoto->clone();
} else {
profilePhoto = nullptr;
}
cout << "简历文档复制完成" << endl;
}
public:
ResumeDocument(const string& docTitle, const string& docAuthor)
: title(docTitle), author(docAuthor), profilePhoto(nullptr) {
createTime = time(nullptr);
modifyTime = createTime;
cout << "创建新简历文档: " << title << endl;
// 复杂的初始化过程
initializeResume();
}
~ResumeDocument() {
for (auto para : sections) {
delete para;
}
delete profilePhoto;
}
// 复杂的初始化方法
void initializeResume() {
// 添加简历的标准段落
Paragraph* para1 = new Paragraph("个人简介:拥有8年软件开发经验的工程师",
"微软雅黑", 11, "left");
sections.push_back(para1);
Paragraph* para2 = new Paragraph("工作经历:2018-2024 某科技公司 高级工程师\n"
"负责核心系统的设计与开发,带领5人团队完成多个重要项目",
"微软雅黑", 11, "left");
sections.push_back(para2);
Paragraph* para3 = new Paragraph("教育背景:某大学 计算机科学与技术 硕士 2015-2018",
"微软雅黑", 11, "left");
sections.push_back(para3);
Paragraph* para4 = new Paragraph("专业技能:C++, Java, Python, 系统设计, 架构规划",
"微软雅黑", 11, "left", true); // 粗体
sections.push_back(para4);
// 添加默认头像
profilePhoto = new Image("/images/default_avatar.png", 150, 150, "个人照片");
}
DocumentPrototype* clone() const override {
return new ResumeDocument(*this);
}
void display() const override {
cout << "\n=== 简历文档 ===" << endl;
cout << "标题: " << title << endl;
cout << "作者: " << author << endl;
cout << "创建时间: " << ctime(&createTime);
cout << "修改时间: " << ctime(&modifyTime);
cout << "内容结构:" << endl;
if (profilePhoto) {
profilePhoto->display();
}
for (size_t i = 0; i < sections.size(); ++i) {
cout << " 段落 " << i + 1 << ":" << endl;
sections[i]->display();
}
}
string getType() const override {
return "Resume";
}
void setTitle(const string& newTitle) override {
title = newTitle;
modifyTime = time(nullptr);
}
void modifyContent() override {
// 模拟内容修改
if (!sections.empty()) {
sections[0]->setText("个人简介:更新后的个人简介,拥有10年软件开发经验...");
}
modifyTime = time(nullptr);
cout << "简历内容已修改" << endl;
}
};
// ========== 具体原型:报告文档 ==========
class ReportDocument : public DocumentPrototype {
private:
string title;
string author;
time_t createTime;
time_t modifyTime;
vector<Paragraph*> chapters;
vector<Image*> figures;
string reportPeriod;
ReportDocument(const ReportDocument& other)
: title(other.title), author(other.author),
createTime(other.createTime), modifyTime(time(nullptr)),
reportPeriod(other.reportPeriod) {
cout << "开始复制报告文档..." << endl;
// 深拷贝章节
for (const auto& chap : other.chapters) {
chapters.push_back(chap->clone());
}
// 深拷贝图表
for (const auto& fig : other.figures) {
figures.push_back(fig->clone());
}
cout << "报告文档复制完成" << endl;
}
public:
ReportDocument(const string& docTitle, const string& docAuthor, const string& period)
: title(docTitle), author(docAuthor), reportPeriod(period) {
createTime = time(nullptr);
modifyTime = createTime;
cout << "创建新报告文档: " << title << endl;
// 复杂的初始化过程
initializeReport();
}
~ReportDocument() {
for (auto chap : chapters) {
delete chap;
}
for (auto fig : figures) {
delete fig;
}
}
void initializeReport() {
// 添加报告的章节
Paragraph* chap1 = new Paragraph("第一章 引言\n本报告旨在分析...",
"Times New Roman", 12, "left");
chapters.push_back(chap1);
Paragraph* chap2 = new Paragraph("第二章 数据分析\n数据显示...",
"Times New Roman", 12, "left");
chapters.push_back(chap2);
Paragraph* chap3 = new Paragraph("第三章 结论与建议\n基于以上分析...",
"Times New Roman", 12, "left", false, true); // 斜体
chapters.push_back(chap3);
// 添加图表
figures.push_back(new Image("/charts/data_analysis.png", 600, 400, "图1 数据分析结果"));
figures.push_back(new Image("/charts/trend_chart.png", 600, 400, "图2 趋势图"));
}
DocumentPrototype* clone() const override {
return new ReportDocument(*this);
}
void display() const override {
cout << "\n=== 报告文档 ===" << endl;
cout << "标题: " << title << endl;
cout << "作者: " << author << endl;
cout << "报告期间: " << reportPeriod << endl;
cout << "创建时间: " << ctime(&createTime);
cout << "修改时间: " << ctime(&modifyTime);
cout << "内容结构:" << endl;
cout << "章节:" << endl;
for (size_t i = 0; i < chapters.size(); ++i) {
chapters[i]->display();
}
cout << "图表:" << endl;
for (size_t i = 0; i < figures.size(); ++i) {
figures[i]->display();
}
}
string getType() const override {
return "Report";
}
void setTitle(const string& newTitle) override {
title = newTitle;
modifyTime = time(nullptr);
}
void modifyContent() override {
// 模拟内容修改
if (!chapters.empty()) {
Paragraph* updatedChap = new Paragraph("第二章 数据分析(更新版)\n最新数据显示...",
"Times New Roman", 12, "left");
delete chapters[1];
chapters[1] = updatedChap;
}
modifyTime = time(nullptr);
cout << "报告内容已修改" << endl;
}
};
// ========== 原型管理器 ==========
class DocumentManager {
private:
unordered_map<string, DocumentPrototype*> prototypes;
public:
~DocumentManager() {
for (auto& pair : prototypes) {
delete pair.second;
}
}
// 注册原型
void registerPrototype(const string& key, DocumentPrototype* prototype) {
prototypes[key] = prototype;
cout << "注册原型: " << key << endl;
}
// 通过原型创建新文档
DocumentPrototype* createDocument(const string& key) {
auto it = prototypes.find(key);
if (it != prototypes.end()) {
cout << "通过原型创建新文档: " << key << endl;
return it->second->clone();
}
return nullptr;
}
// 获取所有可用的原型类型
void listPrototypes() const {
cout << "\n可用的文档原型:" << endl;
for (const auto& pair : prototypes) {
cout << " - " << pair.first << " (" << pair.second->getType() << ")" << endl;
}
}
};
// ========== 客户端代码 ==========
int main() {
cout << "=== 文档管理系统 ===\n" << endl;
// 创建原型管理器
DocumentManager manager;
// 创建并注册原型文档
cout << "1. 创建原型文档:" << endl;
ResumeDocument* resumeProto = new ResumeDocument("通用简历模板", "系统管理员");
ReportDocument* reportProto = new ReportDocument("季度报告模板", "系统管理员", "2024-Q1");
manager.registerPrototype("standard_resume", resumeProto);
manager.registerPrototype("quarter_report", reportProto);
// 查看可用的原型
manager.listPrototypes();
// 使用原型创建新文档
cout << "\n2. 用户创建新文档:" << endl;
// 用户A创建简历
DocumentPrototype* userResume = manager.createDocument("standard_resume");
userResume->setTitle("张三的简历");
userResume->modifyContent();
// 用户B创建简历(快速复制)
DocumentPrototype* userResume2 = manager.createDocument("standard_resume");
userResume2->setTitle("李四的简历");
// 用户C创建报告
DocumentPrototype* userReport = manager.createDocument("quarter_report");
userReport->setTitle("2024年第二季度销售报告");
// 显示所有文档
cout << "\n3. 最终文档内容:" << endl;
userResume->display();
userResume2->display();
userReport->display();
// 清理资源
delete userResume;
delete userResume2;
delete userReport;
// 注意:原型对象会在DocumentManager析构时自动删除
cout << "\n=== 演示深拷贝效果 ===" << endl;
// 演示深拷贝:修改原型不影响克隆对象
DocumentManager testManager;
ResumeDocument* originalResume = new ResumeDocument("原始简历", "测试用户");
testManager.registerPrototype("test", originalResume);
cout << "\n创建克隆简历..." << endl;
DocumentPrototype* clonedResume = testManager.createDocument("test");
cout << "\n修改原始简历..." << endl;
originalResume->modifyContent();
cout << "\n比较原始和克隆:" << endl;
cout << "原始简历:" << endl;
originalResume->display();
cout << "\n克隆简历(不受影响):" << endl;
clonedResume->display();
delete clonedResume;
return 0;
}
7 总结
不使用原型模式的坏处
如果不使用原型模式,可能会采用以下几种实现方式:
方式1:每次都重新创建和初始化
cpp
class DocumentEditor {
public:
// 用户想要创建一个新简历
ResumeDocument* createResumeForUser(const string& userName) {
// 每次都从头开始创建和初始化
ResumeDocument* resume = new ResumeDocument();
// 重复复杂的初始化过程
resume->setTitle(userName + "的简历");
resume->addParagraph(new Paragraph("个人简介:..."));
resume->addParagraph(new Paragraph("工作经历:..."));
resume->addParagraph(new Paragraph("教育背景:..."));
resume->addParagraph(new Paragraph("专业技能:..."));
resume->setProfilePhoto(new Image("/images/default_avatar.png"));
// 还需要设置各种格式
// ... 大量重复代码
return resume;
}
};
问题:
每次创建都需要重复复杂的初始化过程
代码重复,维护困难
如果初始化逻辑变化,需要修改多处代码
性能开销大,特别是创建复杂对象时
方式2:使用拷贝构造函数但暴露给客户端
cpp
class ResumeDocument {
public:
// 公开拷贝构造函数
ResumeDocument(const ResumeDocument& other) {
// 需要手动实现深拷贝
this->title = other.title;
this->author = other.author;
// 容易出错的深拷贝
for (const auto& para : other.sections) {
this->sections.push_back(new Paragraph(*para)); // 需要Paragraph也支持拷贝
}
// 需要处理各种成员
}
};
// 客户端使用
ResumeDocument* original = new ResumeDocument("模板", "系统");
ResumeDocument* copy = new ResumeDocument(*original); // 客户端直接使用拷贝构造
问题:
客户端需要知道具体类的拷贝构造函数
破坏了封装性
客户端代码依赖于具体类
难以管理复杂的拷贝逻辑
无法通过接口统一操作
方式3:使用工厂方法但需要多个工厂
cpp
class ResumeFactory {
public:
ResumeDocument* createResume() {
return new ResumeDocument(); // 还是需要完整的初始化
}
};
class ReportFactory {
public:
ReportDocument* createReport() {
return new ReportDocument(); // 同样的问题
}
};
// 每个文档类型都需要对应的工厂
// 客户端代码复杂
问题:
工厂类数量可能很多
仍然需要处理复杂的初始化
无法实现动态类型的复制
每个工厂都需要知道如何初始化对应的产品
原型模式的优势
1 简化对象创建:隐藏了对象创建的复杂性
2 性能提升:克隆通常比new+初始化更高效
3 减少代码重复:初始化逻辑集中在原型中
4 动态配置:可以在运行时注册和移除原型
5 封装性好:客户端不需要知道具体类名
6 深拷贝控制:可以在原型类中精细控制拷贝深度
8 适用场景
1 对象初始化操作复杂,耗费资源
2 需要创建多个相似对象
3 系统需要独立于产品的创建方式
4 类实例化需要在运行时确定
5 需要避免工厂类层次结构
6 状态相对稳定的对象需要频繁创建
9 原型模式 vs 其他创建型模式

原型模式通过克隆的方式创建对象,特别适合那些创建成本高、需要保持状态或需要动态配置的场景。它提供了一种比传统创建方式更灵活、更高效的对象创建机制。