原型模式(Prototype Pattern)

C++ 原型模式(Prototype Pattern)


目录

  1. 模式定义 & 核心意图
  2. 解决的核心痛点
  3. 模式结构 & 四大角色详解
  4. 浅拷贝 VS 深拷贝 底层原理
  5. C++ 四种完整实现方案
  6. 原型注册管理器(工业级必备)
  7. 原型模式 三大变种
  8. 原型模式 VS 拷贝构造 VS 赋值运算符
  9. 原型模式 VS 工厂模式 VS 建造者模式
  10. 适用场景 & 禁用场景
  11. 优缺点深度拆解
  12. C++ 开发最佳实践规范
  13. 常见坑点 + 完整避坑方案
  14. 面试高频深挖题(含标准答案)
  15. C++ 实际项目落地场景

1. 模式定义 & 核心意图

定义

原型模式是创建型设计模式 ,用一个已有的对象作为原型 ,通过克隆复制原型对象 来生成新对象,不通过 new 调用构造函数从头创建

核心意图

  • 对象创建具体创建逻辑解耦
  • 通过复制现有实例,批量生成相似对象
  • 支持运行时动态指定要克隆的原型
  • 规避复杂对象多次初始化的性能开销

设计思想一句话

拿现成模板复制一份,而非从零造一个。


2. 解决的核心痛点

  1. 对象创建代价极大
    初始化需要读取配置、加载文件、网络请求、数据库查询、复杂计算。
  2. 需要大量属性高度相似的对象
    每个对象仅少量字段不同,全部 new 初始化冗余严重。
  3. 不想暴露复杂构造参数
    构造函数参数过多、内部结构复杂,不希望外部依赖构造签名。
  4. 多态场景下动态创建子类对象
    父类指针无法直接 new 子类,通过克隆实现多态创建。
  5. 需要对象状态快照、撤销重做
    保存当前对象状态,后续克隆恢复。

3. 模式结构 & 四大角色详解

角色 名称 职责说明
Prototype 抽象原型类 声明纯虚 clone() 克隆接口,提供统一抽象层
ConcretePrototype 具体原型类 实现抽象克隆接口,完成深拷贝逻辑
PrototypeManager 原型管理器 统一注册、缓存、获取原型,按标识克隆(扩展角色)
Client 客户端 持有原型/从管理器获取原型,调用 clone 生成新对象

经典标准结构只有前三者,原型管理器是工程必备扩展


4. 浅拷贝 VS 深拷贝 底层原理

4.1 浅拷贝(Shallow Copy)

仅复制对象栈上内存指针/引用成员只复制地址,不分配新内存。

  • 多个对象共享同一块堆内存
  • 修改一个对象,另一个受影响
  • 析构时重复释放内存 → 程序崩溃
  • 原型模式严禁使用浅拷贝

4.2 深拷贝(Deep Copy)

递归复制所有层级 成员:基础类型、容器、指针、嵌套对象,重新分配堆内存

  • 克隆对象与原型完全独立
  • 修改互不干扰
  • 析构各自释放自己内存,无冲突
  • 原型模式必须强制深拷贝

4.3 C++ 中触发拷贝的方式

  • 自定义拷贝构造函数
  • 重载赋值运算符 operator=
  • 实现 clone() 虚接口

5. C++ 四种完整实现方案

5.1 基础版:抽象原型 + 具体原型(最简标准实现)

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

// 抽象原型
class Prototype
{
public:
    virtual ~Prototype() = default;
    // 纯虚克隆接口
    virtual std::unique_ptr<Prototype> clone() const = 0;
    virtual void showInfo() const = 0;
};

// 具体原型:文档对象
class Document : public Prototype
{
private:
    std::string title;
    std::string content;
public:
    Document(std::string t, std::string c)
        : title(std::move(t)), content(std::move(c)) {}

    // 深拷贝构造函数
    Document(const Document& other)
        : title(other.title), content(other.content) {}

    // 实现克隆
    std::unique_ptr<Prototype> clone() const override
    {
        return std::make_unique<Document>(*this);
    }

    void showInfo() const override
    {
        std::cout << "标题:" << title 
                  << " 内容:" << content << std::endl;
    }
};

使用示例:

cpp 复制代码
int main()
{
    // 创建原型模板
    std::unique_ptr<Prototype> proto = 
        std::make_unique<Document>("设计模式", "原型模式详解");

    // 克隆新对象
    auto doc1 = proto->clone();
    auto doc2 = proto->clone();

    doc1->showInfo();
    doc2->showInfo();
    return 0;
}

5.2 进阶版:含指针成员的深拷贝实现

适合有原始指针、智能指针、嵌套对象的复杂结构。

cpp 复制代码
class Page
{
public:
    std::string pageName;
    Page(std::string n) : pageName(std::move(n)) {}
};

class Book : public Prototype
{
private:
    std::string bookName;
    Page* page;  // 原始指针成员
public:
    Book(std::string name, Page* p)
        : bookName(std::move(name)), page(p) {}

    // 深拷贝:重新new子对象
    Book(const Book& other)
        : bookName(other.bookName),
          page(new Page(other.page->pageName)) {}

    ~Book()
    {
        delete page;
    }

    std::unique_ptr<Prototype> clone() const override
    {
        return std::make_unique<Book>(*this);
    }

    void showInfo() const override
    {
        std::cout << "书籍:" << bookName 
                  << " 章节:" << page->pageName << std::endl;
    }
};

5.3 C++11 简化版:无抽象类 直接克隆

简单业务无需多态,省略抽象基类,轻量化使用:

cpp 复制代码
class Student
{
private:
    std::string name;
    int age;
public:
    Student(std::string n, int a) : name(std::move(n)), age(a) {}

    // 拷贝构造
    Student(const Student& other) = default;

    // 克隆方法
    Student clone() const
    {
        return *this;
    }

    void print() const
    {
        std::cout << name << " " << age << std::endl;
    }
};

5.4 高性能版:CRTP 模板原型(无虚函数开销)

无需虚函数、无动态多态,零运行时开销,适合高性能服务:

cpp 复制代码
template<class Derived>
class Cloneable
{
public:
    std::unique_ptr<Derived> clone() const
    {
        return std::make_unique<Derived>(static_cast<const Derived&>(*this));
    }
};

// 继承模板即可拥有克隆能力
class Config : public Cloneable<Config>
{
public:
    std::string ip;
    int port;
    Config(std::string i, int p) : ip(std::move(i)), port(p) {}
};

6. 原型注册管理器(工业级必备)

统一管理所有原型模板,按 key 注册、按 key 克隆,全局单例使用。

cpp 复制代码
#include <unordered_map>
#include <stdexcept>

class PrototypeManager
{
private:
    std::unordered_map<std::string, std::unique_ptr<Prototype>> protoMap;
    // 单例
    PrototypeManager() = default;
public:
    // 禁止拷贝
    PrototypeManager(const PrototypeManager&) = delete;
    PrototypeManager& operator=(const PrototypeManager&) = delete;

    static PrototypeManager& getInstance()
    {
        static PrototypeManager inst;
        return inst;
    }

    // 注册原型
    void registerProto(const std::string& key, std::unique_ptr<Prototype> proto)
    {
        protoMap[key] = std::move(proto);
    }

    // 根据key克隆
    std::unique_ptr<Prototype> cloneProto(const std::string& key)
    {
        auto it = protoMap.find(key);
        if (it == protoMap.end())
        {
            throw std::runtime_error("不存在该原型");
        }
        return it->second->clone();
    }

    // 移除原型
    void removeProto(const std::string& key)
    {
        protoMap.erase(key);
    }
};

使用方式:

cpp 复制代码
// 注册
PrototypeManager::getInstance().registerProto("doc_template", 
    std::make_unique<Document>("模板文档", "通用内容"));

// 克隆
auto newDoc = PrototypeManager::getInstance().cloneProto("doc_template");

7. 原型模式三大变种

  1. 简单原型模式
    无抽象基类,直接在具体类写 clone(),适合小型项目。
  2. 多原型模式
    管理器存放多个不同类型原型,按需选取克隆。
  3. 惰性初始化原型
    原型不提前创建,第一次克隆时才初始化加载,节省内存。

8. 原型模式 VS 拷贝构造 VS 赋值重载

对比项 原型模式 clone() 拷贝构造函数 赋值运算符 operator=
多态支持 ✅ 支持多态克隆 ❌ 静态绑定 ❌ 静态绑定
创建新对象 ✅ 生成全新对象 ✅ 生成全新对象 ❌ 覆盖已有对象
接口统一 ✅ 统一虚接口 ❌ 每个类独立 ❌ 每个类独立
运行时动态 ✅ 可运行时选原型 ❌ 编译期确定 ❌ 编译期确定
适用场景 框架、多态、管理器 普通对象拷贝 对象赋值覆盖

结论

多态/框架/统一接口 → 用原型模式;

普通简单对象拷贝 → 直接用拷贝构造。


9. 原型 VS 工厂 VS 建造者

模式 核心作用 适用场景
原型模式 复制已有对象创建新对象 对象创建昂贵、大量相似对象、状态快照
工厂模式 按类型生产不同产品 产品线固定、按类型区分子类
建造者模式 分步组装复杂对象 多可选参数、流式配置、构造臃肿

选型口诀

  • 复制模板、省初始化 → 原型
  • 按类型造产品 → 工厂
  • 多参数分步配置 → 建造者

10. 适用场景 & 禁用场景

适用场景

  1. 对象初始化耗时久(文件、网络、DB、复杂计算)。
  2. 系统需要大量相似配置对象。
  3. 隐藏对象创建细节,不对外暴露构造函数。
  4. 需要多态动态创建子类实例。
  5. 需做对象状态备份、快照、撤销恢复。
  6. 配置模板、协议模板、游戏怪物/子弹批量生成。

禁用场景

  1. 对象结构极简单、成员少于3个。
  2. 对象无任何初始化开销,直接 new 成本极低。
  3. 对象存在复杂循环引用,深拷贝极难实现。

11. 优缺点深度拆解

优点

  1. 性能高:规避重复昂贵初始化,直接复制内存。
  2. 多态支持:父类接口克隆子类,扩展性强。
  3. 解耦创建逻辑:客户端不用关心对象构造细节。
  4. 运行时动态:可随时新增、替换、删除原型。
  5. 简化代码:替代爆炸式构造函数、大量重载。

缺点

  1. 深拷贝实现成本高:嵌套对象、指针、容器需要手动处理。
  2. 循环引用难以处理:会导致递归拷贝死循环。
  3. 每增加一个具体类,都要实现 clone() 接口。
  4. 对于简单对象,存在过度设计

12. C++ 开发最佳实践

  1. 优先使用 std::unique_ptr 管理原型,避免裸指针内存泄漏。
  2. 复杂对象必须手动实现深拷贝,禁止默认浅拷贝。
  3. 框架级项目必须引入原型管理器 + 单例
  4. 多态场景用虚函数 clone ,高性能场景用 CRTP 模板原型
  5. 遵循 C++ 三五法则:自定义拷贝构造就要同步处理析构、赋值重载。
  6. 原型对象设置为只读模板,业务克隆后再修改属性。
  7. 禁止在原型模板上直接修改,避免污染全局模板。

13. 常见坑点 + 完整避坑

  1. 坑:使用默认浅拷贝

    后果:共享堆内存、析构崩溃、数据错乱。

    避坑:手动实现深拷贝构造,指针成员手动 new 复制。

  2. 坑:派生类未重写 clone

    后果:克隆出父类对象,丢失子类属性。

    避坑:每个子类必须覆写 clone()

  3. 坑:原型模板被业务修改

    后果:后续克隆全部被污染。

    避坑:原型设为 const,只克隆后再修改。

  4. 坑:循环引用对象深拷贝递归死循环

    避坑:用 std::weak_ptr 打破循环依赖。

  5. 坑:简单对象强行上原型模式

    避坑:成员少、创建成本低,直接 new 即可。


14. 面试高频深挖题(标准答案)

Q1:原型模式的核心原理是什么?

通过克隆现有原型对象创建新实例,而非调用构造函数重新初始化,分离创建逻辑与业务逻辑,支持多态、提升性能。

Q2:为什么原型模式必须用深拷贝?

浅拷贝仅复制地址,多个对象共享堆内存,修改互相影响、析构重复释放崩溃;深拷贝为所有成员重新分配内存,对象完全独立。

Q3:原型模式和拷贝构造函数有什么区别?

拷贝构造是编译期静态绑定 ,不支持多态;原型模式通过虚函数 clone() 运行时多态,有统一抽象接口,可配合管理器批量管理模板。

Q4:原型管理器的作用?

集中注册、缓存各类原型模板,全局统一获取、按标识克隆,实现原型复用、动态替换、统一管理。

Q5:什么时候用原型而不用工厂?

对象创建开销大、大量相似实例、需要状态快照、运行时动态生成相似对象时选原型;按类型生产不同子类产品选工厂。


15. C++ 实际项目落地场景

  1. 游戏开发:怪物、NPC、子弹、道具批量克隆生成。
  2. 配置中心:服务配置、数据库配置模板克隆。
  3. 网络协议:报文模板、请求头模板复制。
  4. 文档/报表系统:模板文档批量生成。
  5. GUI 框架:窗口、控件样式模板克隆。
  6. 业务中间件:连接池对象、会话对象快照复用。
  7. 编辑器:对象撤销、重做、历史状态快照。
相关推荐
栉甜1 天前
Js进阶(4)
开发语言·javascript·原型模式
UXbot2 天前
AI画原型工具如何帮非设计师快速生成UI界面
前端·vue.js·ui·kotlin·swift·原型模式·web app
UXbot2 天前
2026年文字转原型AI工具推荐:输入一句需求描述,自动生成多页面可交互界面
前端·低代码·ui·交互·ai编程·原型模式
UXbot2 天前
AI应用原型平台核心能力:界面自动生成、交互流程编辑、多格式代码导出详解
前端·低代码·交互·软件构建·原型模式·web app
蜡笔小马3 天前
03.C++设计模式-原型模式
c++·设计模式·原型模式
宁雨桥6 天前
前端修行日记之JS 原型与 AI基础常识
前端·javascript·原型模式
清水白石0088 天前
生成器不是性能银弹:什么时候该用 `yield` 省内存,什么时候它会拖慢 Python 数据处理吞吐?
开发语言·python·原型模式
梦想画家8 天前
唤醒沉睡的数据:ERP销售模块接入Apache AGE实战指南
知识图谱·原型模式
Alex艾力的IT数字空间9 天前
再思“把事情做对”与“把事情做好”的辩证关系与先后顺序
信息可视化·需求分析·学习方法·抽象工厂模式·远程工作·原型模式·中介者模式