C++设计模式之创建型模式:原型模式(Prototype)

原型模式(Prototype)是创建型设计模式的一种,它通过复制现有对象(原型)来创建新对象,而无需重新初始化,从而简化复杂对象的创建过程并提高效率。这种模式特别适合创建成本高(如初始化步骤多、资源消耗大)或配置复杂的对象。

一、核心思想与角色

原型模式的核心是"通过克隆生成新对象",避免了重复的初始化逻辑。其核心角色如下:

角色名称 核心职责
抽象原型(Prototype) 声明克隆方法的接口(通常是纯虚函数clone()),所有具体原型都需实现该接口。
具体原型(Concrete Prototype) 实现clone()方法,通过复制自身创建新对象(深拷贝或浅拷贝)。
客户端(Client) 通过调用原型的clone()方法创建新对象,无需知道具体类名。

关键优势 :当对象创建复杂时,克隆比重新new并初始化更高效;同时,客户端可动态生成新对象,无需与具体类耦合。

二、实现示例(文档复制场景)

假设我们需要创建多种文档(如文本文档、表格文档),这些文档包含复杂的格式和内容配置。使用原型模式通过克隆快速创建新文档:

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 1. 抽象原型:文档
class Document {
public:
    // 声明克隆方法(返回自身的副本)
    virtual Document* clone() const = 0;
    // 文档操作
    virtual void setContent(const std::string& content) { m_content = content; }
    virtual void addFormat(const std::string& format) { m_formats.push_back(format); }
    virtual void showInfo() const = 0;
    virtual ~Document() = default;

protected:
    std::string m_content;       // 内容
    std::vector<std::string> m_formats; // 格式配置(如字体、颜色等)
};

// 2. 具体原型1:文本文档
class TextDocument : public Document {
public:
    // 实现克隆方法:深拷贝自身
    Document* clone() const override {
        TextDocument* copy = new TextDocument();
        copy->m_content = this->m_content;       // 复制内容
        copy->m_formats = this->m_formats;       // 复制格式(深拷贝vector)
        return copy;
    }

    void showInfo() const override {
        std::cout << "文本文档 - 内容: " << m_content << std::endl;
        std::cout << "格式: ";
        for (const auto& f : m_formats) {
            std::cout << f << " ";
        }
        std::cout << std::endl;
    }
};

// 2. 具体原型2:表格文档
class SpreadsheetDocument : public Document {
private:
    int m_rowCount;  // 表格特有属性:行数
    int m_colCount;  // 列数

public:
    SpreadsheetDocument(int rows = 0, int cols = 0) 
        : m_rowCount(rows), m_colCount(cols) {}

    // 实现克隆方法:复制所有属性(包括特有属性)
    Document* clone() const override {
        SpreadsheetDocument* copy = new SpreadsheetDocument();
        copy->m_content = this->m_content;
        copy->m_formats = this->m_formats;
        copy->m_rowCount = this->m_rowCount;  // 复制表格特有属性
        copy->m_colCount = this->m_colCount;
        return copy;
    }

    void setRowCol(int rows, int cols) {
        m_rowCount = rows;
        m_colCount = cols;
    }

    void showInfo() const override {
        std::cout << "表格文档 - 内容: " << m_content << std::endl;
        std::cout << "行列: " << m_rowCount << "x" << m_colCount << std::endl;
        std::cout << "格式: ";
        for (const auto& f : m_formats) {
            std::cout << f << " ";
        }
        std::cout << std::endl;
    }
};

// 客户端代码:通过克隆创建新文档
int main() {
    // 1. 创建原型文档(复杂初始化)
    TextDocument* textProto = new TextDocument();
    textProto->setContent("原型文本文档");
    textProto->addFormat("字体: 宋体");
    textProto->addFormat("字号: 12");

    SpreadsheetDocument* sheetProto = new SpreadsheetDocument();
    sheetProto->setContent("原型表格文档");
    sheetProto->setRowCol(10, 5);
    sheetProto->addFormat("边框: 实线");
    sheetProto->addFormat("对齐: 居中");

    // 2. 克隆原型创建新文档(无需重新配置)
    Document* textCopy1 = textProto->clone();
    textCopy1->setContent("克隆的文本文档1");  // 修改内容,不影响原型

    Document* textCopy2 = textProto->clone();
    textCopy2->setContent("克隆的文本文档2");
    textCopy2->addFormat("颜色: 红色");       // 新增格式

    Document* sheetCopy = sheetProto->clone();
    sheetCopy->setContent("克隆的表格文档");
    sheetCopy->setRowCol(8, 4);              // 修改表格行列

    // 3. 展示所有文档
    std::cout << "=== 原型文档 ===" << std::endl;
    textProto->showInfo();
    sheetProto->showInfo();

    std::cout << "\n=== 克隆文档 ===" << std::endl;
    textCopy1->showInfo();
    textCopy2->showInfo();
    sheetCopy->showInfo();

    // 4. 释放资源
    delete textProto;
    delete sheetProto;
    delete textCopy1;
    delete textCopy2;
    delete sheetCopy;

    return 0;
}

三、代码解析

  1. 抽象原型(Document)

    定义了克隆接口clone()和文档通用操作(setContent()addFormat()),包含所有文档共有的属性(内容、格式)。

  2. 具体原型

    • TextDocument:实现clone()方法,通过深拷贝复制自身的内容和格式。
    • SpreadsheetDocument:除复制通用属性外,还克隆表格特有的行列属性,确保克隆对象的独立性。
  3. 克隆逻辑

    关键是实现深拷贝 (而非浅拷贝),确保克隆对象与原型对象完全独立(修改克隆对象不会影响原型)。例如,m_formatsvector,直接赋值会复制所有元素(深拷贝);若包含指针成员,则需手动复制指针指向的内容。

  4. 客户端使用

    客户端只需通过原型的clone()方法创建新对象,无需知道具体文档类型(如TextDocument),只需依赖Document接口,符合依赖倒置原则。

四、深拷贝与浅拷贝

原型模式的核心是"正确克隆对象",需区分两种拷贝方式:

  • 浅拷贝 :仅复制对象本身及基本类型成员,指针成员仅复制地址(与原型共享内存)。

    风险:修改克隆对象的指针成员会影响原型,可能导致内存泄漏。

  • 深拷贝 :不仅复制对象本身,还递归复制所有指针成员指向的内容(完全独立的副本)。

    实现:在clone()中手动为指针成员分配新内存并复制数据,或使用智能指针(如std::unique_ptr)管理动态资源。

示例(含指针成员的深拷贝)

cpp 复制代码
class MyClass : public Prototype {
private:
    int* data; // 指针成员

public:
    MyClass(int val) : data(new int(val)) {}

    // 深拷贝实现
    MyClass* clone() const override {
        MyClass* copy = new MyClass(0);
        copy->data = new int(*this->data); // 复制指针指向的内容
        return copy;
    }

    ~MyClass() { delete data; } // 释放资源
};

五、适用场景与优势

适用场景
  1. 当对象创建成本高(如需要读取配置文件、数据库连接),克隆比重新创建更高效。
  2. 当需要动态生成多种相似对象(如不同配置的文档、游戏角色),且不想硬编码具体类。
  3. 当对象的结构复杂,初始化步骤多,希望简化创建过程时。
核心优势
  1. 高效性:克隆避免了重复的初始化逻辑,尤其适合创建成本高的对象。
  2. 灵活性:客户端可通过克隆动态生成新对象,无需与具体类耦合。
  3. 简化创建:无需记住复杂的创建参数,直接基于原型修改即可。

六、与其他创建型模式的区别

模式 核心差异点
原型模式 通过克隆现有对象创建新对象,适合复杂对象的快速复制。
工厂方法 通过子类决定创建哪种对象,适合产品类型固定但需扩展的场景。
建造者模式 分步构建复杂对象,关注构建流程和部件组合,适合配置差异大的对象。
单例模式 确保对象唯一,与原型模式的"复制多实例"目标相反。

七、实践建议

  1. 优先深拷贝 :除非明确需要共享资源,否则clone()方法应实现深拷贝,避免对象间的意外影响。
  2. 结合工厂模式 :可创建"原型工厂"管理多个原型,客户端通过工厂获取克隆对象(如PrototypeFactory::getClone("text"))。
  3. 使用智能指针 :在现代C++中,用std::unique_ptrstd::shared_ptr管理克隆对象,避免内存泄漏。
  4. 标记接口 :抽象原型可继承一个空接口(如IPrototype),仅用于标识该类支持克隆。

原型模式的核心价值在于"通过复制实现高效、灵活的对象创建"。当系统中存在大量相似对象或创建成本高的对象时,使用原型模式能显著提升性能并简化代码,是创建型模式中解决"对象复制"问题的最佳方案。

相关推荐
今天也好累5 小时前
贪心算法之会议安排问题
c++·笔记·学习·算法·贪心算法
phdsky9 小时前
【设计模式】中介者模式
c++·设计模式·中介者模式
洲覆9 小时前
C++ constexpr 修饰符与函数
开发语言·数据结构·c++
我是华为OD~HR~栗栗呀10 小时前
华为od-前端面经-22届非科班
java·前端·c++·后端·python·华为od·华为
phdsky12 小时前
【设计模式】状态模式
设计模式·状态模式
bkspiderx12 小时前
C++设计模式之创建型模式:抽象工厂模式(Abstract Factory)
c++·设计模式·抽象工厂模式
Chan1612 小时前
【 设计模式 | 创建型模式 建造者模式 】
java·spring boot·设计模式·java-ee·intellij-idea·建造者模式
嵌入式小李.man12 小时前
C++第十篇:const关键字
开发语言·c++
郝学胜-神的一滴12 小时前
基于Linux,看清C++的动态库和静态库
linux·服务器·开发语言·c++·程序人生