原型模式

. 原型模式 (Prototype Pattern)

场景设定:游戏中的怪物军团

假设你在开发一个 RPG 游戏,需要生成 1000 个“兽人步兵”。

  • 每个兽人初始化时,都需要加载模型文件、读取纹理、计算碰撞体积……这个过程非常耗时(假设需要 100ms)。
  • 如果用 new Orc() 循环 1000 次,加载时间就是 100秒,游戏直接卡死。
  • 解决方案: 先创建一个“兽人原型”,配置好所有属性。然后剩下的 999 个,直接克隆这个原型(内存拷贝),速度极快。

代码示例

#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

// ==========================================
// 1. 抽象原型 (Prototype Interface)
// 核心:定义 Clone 接口
// ==========================================
class Monster {
public:
    virtual ~Monster() {}
    
    // 【核心方法】 clone
    // 返回基类指针,利用多态机制
    virtual Monster* clone() = 0;

    virtual void Show() = 0;
};

// ==========================================
// 2. 具体原型 (Concrete Prototype)
// 变化点:不同的怪物类型
// ==========================================

class Orc : public Monster {
private:
    string name;
    int hp;
    // 假设这是一个很耗资源的指针(模拟深拷贝需求)
    int* weaponAttr; 

public:
    // 构造函数:模拟耗时的初始化
    Orc(string n, int h, int w) : name(n), hp(h) {
        // 假设这里进行了昂贵的资源加载...
        weaponAttr = new int(w); 
        cout << "  [系统] " << name << " 模型加载完毕 (耗时操作)" << endl;
    }

    // 【关键点 1】拷贝构造函数 (Copy Constructor)
    // C++ 中实现原型的核心!必须处理深拷贝!
    Orc(const Orc& other) {
        this->name = other.name;
        this->hp = other.hp;
        // 深拷贝:重新申请内存,复制数值,而不是复制指针地址
        if (other.weaponAttr) {
            this->weaponAttr = new int(*other.weaponAttr);
        } else {
            this->weaponAttr = nullptr;
        }
        cout << "  [系统] " << name << " 被克隆了 (快速内存拷贝)" << endl;
    }

    ~Orc() {
        if (weaponAttr) delete weaponAttr;
    }

    // 【关键点 2】实现 clone 方法
    // 惯用写法:return new ClassName(*this);
    // 这会调用上面的拷贝构造函数
    Monster* clone() override {
        return new Orc(*this); 
    }

    void Show() override {
        cout << "  -> 兽人: " << name << ", HP: " << hp 
             << ", 攻击力: " << *weaponAttr 
             << " (内存地址: " << this << ")" << endl;
    }
};

class Slime : public Monster {
    // 史莱姆比较简单,使用默认拷贝构造即可(如果是浅拷贝安全的)
public:
    Monster* clone() override {
        return new Slime(*this);
    }
    void Show() override { cout << "  -> 史莱姆分裂了..." << endl; }
};

// ==========================================
// 3. 原型管理器 (Prototype Manager / Factory)
// 可选:用于管理和存储常用的原型
// ==========================================
class MonsterFactory {
private:
    unordered_map<string, Monster*> prototypes;

public:
    ~MonsterFactory() {
        for (auto p : prototypes) delete p.second;
    }

    // 注册原型
    void Register(string key, Monster* m) {
        prototypes[key] = m;
    }

    // 生产对象(通过克隆)
    Monster* Create(string key) {
        if (prototypes.find(key) != prototypes.end()) {
            return prototypes[key]->clone(); // 调用原型的 clone
        }
        return nullptr;
    }
};

// ==========================================
// 客户端代码
// ==========================================
int main() {
    // 1. 初始化阶段:创建种子(原型)
    cout << "=== 游戏初始化:加载原型 ===" << endl;
    Monster* orcPrototype = new Orc("精英兽人", 200, 50);
    
    MonsterFactory factory;
    factory.Register("Orc", orcPrototype);
    factory.Register("Slime", new Slime());

    // 2. 战斗阶段:快速刷怪
    cout << "\n=== 战斗开始:刷怪 ===" << endl;
    
    // 不需要知道 Orc 的构造细节,直接克隆
    Monster* m1 = factory.Create("Orc");
    m1->Show();

    Monster* m2 = factory.Create("Orc");
    m2->Show();

    Monster* s1 = factory.Create("Slime");
    s1->Show();

    delete m1;
    delete m2;
    delete s1;

    return 0;
}

1. 定义

  • 原文: 使用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
  • 解读: 给我一个“模具”,我照着它复制。我不关心它是怎么 new 出来的,我只管 copy。

2. 解决的问题 (Performance & Decoupling)

  • 性能瓶颈 (Performance):
    • 如果对象的创建成本(构造函数执行时间)非常高(如读数据库、读文件、复杂计算),而你又需要大量相同或相似的对象。
    • 克隆(内存块复制)通常比 new + 初始化要快得多。
  • 解耦 (Decoupling):
    • 客户端(Main函数)不需要知道 Orc 的具体类名,也不需要知道 Orc 的构造参数(比如攻击力是50还是100)。它只要拿着 factory 里的原型去 clone 即可。

3. C++ 的核心痛点:深拷贝 vs 浅拷贝

这是 C++ 面试考原型模式时必问的陷阱。

  • 浅拷贝 (Shallow Copy):
    • 默认的拷贝构造函数是浅拷贝。
    • 如果类里有指针(如 int* weaponAttr),浅拷贝只会复制指针的地址
    • 后果: 克隆出来的对象和原对象指向同一块内存。当其中一个对象析构时 (delete weaponAttr),另一个对象的指针就变成了悬垂指针(Dangling Pointer),再次析构会引发 Double Free 崩溃。
  • 深拷贝 (Deep Copy):
    • 必须手写拷贝构造函数。
    • new 一块新的内存,把原指针指向的值拷贝过去。
    • 原则: 原型模式在 C++ 中必须保证深拷贝安全。

4. 符合哪些设计原则?

  • A. 依赖倒置原则 (DIP)
    • 客户端依赖 Monster 接口的 clone 方法,而不依赖具体的 new Orc(...)
  • B. 开闭原则 (OCP)
    • 如果想增加一种新怪物 Dragon,只需要实现 Dragon 并注册到工厂,不需要修改现有刷怪逻辑。

5. 原型模式 vs 工厂模式

这是初学者最容易混淆的地方:

特性 工厂模式 (Factory) 原型模式 (Prototype)
创建方式 使用 new 关键字 使用 clone() 方法
初始化过程 每次都执行完整的构造函数 复制现有内存状态 (跳过复杂初始化)
状态 创建出的对象通常是“出厂设置” 创建出的对象保留了原型运行时的状态
适用场景 对象创建简单,或每次都需要全新状态 对象创建昂贵,或需要大量相似对象

💡 总结

原型模式的核心就一句话:

当你发现 new 一个对象太慢、太麻烦,或者你根本不知道你要创建的对象的具体类型(只知道它是一个 Monster*)时,不要创建,直接复制 (Clone)。

C++ 记住这个公式:

Prototype Pattern = virtual clone() + Deep Copy Constructor。

posted @ 2025-12-20 20:46  belief73  阅读(1)  评论(0)    收藏  举报