设计模式之原型模式

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 其他创建型模式

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

相关推荐
逆境不可逃5 小时前
【从零入门23种设计模式17】行为型之中介者模式
java·leetcode·microsoft·设计模式·职场和发展·中介者模式
Anurmy5 小时前
设计模式之抽象工厂
设计模式
蜜獾云6 小时前
设计模式之原型模式:以自己为原型,自己实现自己的对象拷贝逻辑
java·设计模式·原型模式
melonbo6 小时前
C++ 中用于模块间通信的设计模式
开发语言·c++·设计模式
蜜獾云6 小时前
设计模式之简单工厂模式(4):创建对象时不会暴露创建逻辑
java·设计模式·简单工厂模式
geovindu6 小时前
python: Builder Pattern
python·设计模式·建造者模式
蜜獾云6 小时前
设计模式之中介者模式:让互相调用的模块之间解耦合
microsoft·设计模式·中介者模式
absunique7 小时前
算法设计模式看编程思维的抽象能力的技术6
算法·设计模式
蜜獾云7 小时前
设计模式之构造器模式:封装复杂对象的构造逻辑
java·开发语言·设计模式
Anurmy10 小时前
设计模式之桥接模式
设计模式·桥接模式