原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有对象(原型)来创建新对象,而无需知道具体的类信息。这种模式特别适合当创建对象的成本较高(如需要复杂初始化)或需要频繁创建相似对象的场景。

原型模式的核心思想

  • 通过复制已有实例来生成新实例,而非通过new关键字直接创建
  • 被复制的实例称为"原型",新实例是原型的副本
  • 隐藏对象创建的细节,提高创建效率

原型模式的核心角色

  1. 抽象原型(Prototype):定义复制自身的接口(通常是clone()方法)
  2. 具体原型(Concrete Prototype):实现复制自身的方法
  3. 客户端(Client):通过原型的clone()方法创建新对象

原型模式的实现示例

下面以文档复制为例展示原型模式的实现,文档可以包含文本和图片,我们通过原型模式快速复制文档:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 抽象原型类
class DocumentPrototype {
public:
    virtual std::unique_ptr<DocumentPrototype> clone() const = 0;
    virtual void addContent(const std::string& content) = 0;
    virtual void show() const = 0;
    virtual ~DocumentPrototype() = default;
};

// 具体原型类:文本文档
class TextDocument : public DocumentPrototype {
private:
    std::string title;
    std::vector<std::string> paragraphs;

public:
    TextDocument(const std::string& docTitle) : title(docTitle) {}

    // 实现克隆方法
    std::unique_ptr<DocumentPrototype> clone() const override {
        // 创建当前对象的副本并返回
        return std::make_unique<TextDocument>(*this);
    }

    void addContent(const std::string& content) override {
        paragraphs.push_back(content);
    }

    void show() const override {
        std::cout << "文本文档: " << title << std::endl;
        for (size_t i = 0; i < paragraphs.size(); ++i) {
            std::cout << "段落 " << (i + 1) << ": " << paragraphs[i] << std::endl;
        }
    }
};

// 具体原型类:图文文档
class ImageDocument : public DocumentPrototype {
private:
    std::string title;
    std::vector<std::string> texts;
    std::vector<std::string> images;

public:
    ImageDocument(const std::string& docTitle) : title(docTitle) {}

    // 实现克隆方法
    std::unique_ptr<DocumentPrototype> clone() const override {
        return std::make_unique<ImageDocument>(*this);
    }

    void addContent(const std::string& content) override {
        // 简单判断内容是文本还是图片
        if (content.substr(0, 5) == "image") {
            images.push_back(content);
        } else {
            texts.push_back(content);
        }
    }

    void show() const override {
        std::cout << "图文文档: " << title << std::endl;
        for (size_t i = 0; i < texts.size(); ++i) {
            std::cout << "文本 " << (i + 1) << ": " << texts[i] << std::endl;
        }
        for (size_t i = 0; i < images.size(); ++i) {
            std::cout << "图片 " << (i + 1) << ": " << images[i] << std::endl;
        }
    }
};

// 客户端使用
int main() {
    // 创建原型文档
    auto textDoc = std::make_unique<TextDocument>("会议记录");
    textDoc->addContent("项目进度汇报");
    textDoc->addContent("讨论了新功能的实现方案");

    // 克隆文本文档
    auto textDocCopy = textDoc->clone();
    textDocCopy->addContent("补充:下周将进行测试");

    // 创建图文文档原型
    auto imageDoc = std::make_unique<ImageDocument>("产品说明书");
    imageDoc->addContent("产品介绍");
    imageDoc->addContent("image1: 产品正面图");
    imageDoc->addContent("使用方法说明");

    // 克隆图文文档
    auto imageDocCopy = imageDoc->clone();
    imageDocCopy->addContent("image2: 产品侧面图");

    // 展示所有文档
    std::cout << "===== 原始文本文档 =====" << std::endl;
    textDoc->show();
    
    std::cout << "\n===== 复制的文本文档 =====" << std::endl;
    textDocCopy->show();
    
    std::cout << "\n===== 原始图文文档 =====" << std::endl;
    imageDoc->show();
    
    std::cout << "\n===== 复制的图文文档 =====" << std::endl;
    imageDocCopy->show();

    return 0;
}
    

深拷贝与浅拷贝

在原型模式中,克隆对象时需要特别注意拷贝的深度:

  • 浅拷贝:只复制对象本身和基本类型成员,引用类型成员只复制指针(默认拷贝构造函数的行为)
  • 深拷贝:不仅复制对象本身,还会复制所有引用类型成员所指向的资源

在C++中,如果类包含指针成员或动态分配的资源,需要自定义拷贝构造函数和clone()方法来实现深拷贝,避免多个对象共享同一份资源。

原型模式与工厂模式的区别

  • 原型模式:通过复制现有对象创建新对象,适合创建相似但有细微差别的对象
  • 工厂模式:通过工厂类创建新对象,适合根据类型创建不同的对象

原型模式的应用场景

  1. 对象创建成本高(如需要读取数据库、文件等)
  2. 需要频繁创建相似对象(如游戏中的敌人、粒子系统)
  3. 不确定具体类型的情况下创建对象
  4. 避免构造函数的限制,通过复制绕过复杂的初始化过程

原型模式的优缺点

优点

  • 提高对象创建效率,尤其是创建复杂对象时
  • 简化对象创建过程,隐藏创建细节
  • 便于动态添加或删除产品
  • 允许在运行时动态改变具体的产品类型

缺点

  • 需要为每个类实现clone()方法,增加代码维护成本
  • 深拷贝实现复杂,容易出错
  • 可能会隐藏对象创建的具体细节,增加代码理解难度

原型模式在C++标准库中也有应用,例如std::shared_ptrstd::unique_ptr的拷贝行为,以及容器的复制构造函数等,都是原型模式思想的体现。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐