一、原型模式 + 核心定义
关键字:原型、克隆、对象拷贝、解耦、避免重复创建
初学者必懂:原型模式的核心是“用已有对象作为‘原型模板’,通过克隆(拷贝)的方式创建新对象”——不用重新写构造逻辑,直接复制模板的属性和状态,既高效又能避免代码冗余!
先看游戏开发中的经典痛点:你要做一款《圣铠传说》风格的游戏,有幽灵(Ghost)、恶魔(Demon)、巫师(Sorcerer)等多种怪物,每种怪物都需要一个“生产者”(Spawner)来创建实例。如果用传统方法,会发生什么?
传统方案:为每个怪物写一个生产者类
// 1. 定义怪物基类和子类
class Monster {};
class Ghost : public Monster {}; // 幽灵怪物
class Demon : public Monster {}; // 恶魔怪物
class Sorcerer : public Monster {};// 巫师怪物
// 2. 定义生产者基类和子类(平行类结构)
class Spawner {
public:
virtual ~Spawner() {}
virtual Monster* spawnMonster() = 0; // 纯虚函数,子类实现
};
// 幽灵生产者:专门创建幽灵
class GhostSpawner : public Spawner {
public:
Monster* spawnMonster() override {
return new Ghost();
}
};
// 恶魔生产者:专门创建恶魔
class DemonSpawner : public Spawner {
public:
Monster* spawnMonster() override {
return new Demon();
}
};
// 巫师生产者:专门创建巫师
class SorcererSpawner : public Spawner {
public:
Monster* spawnMonster() override {
return new Sorcerer();
}
};
问题分析:① 类爆炸:有 N 种怪物就需要 N 种生产者类,游戏有几十上百种怪物时,类数量会失控;② 冗余代码:每个生产者类的逻辑几乎一样,都是 new 一个怪物对象,重复劳动;③ 维护困难:新增怪物时,既要写怪物类,还要写对应的生产者类,修改逻辑时要改多个地方。
你有一份“模板文件”(原型对象),想多复印几份:① 不用重新手写文件(避免重复构造);② 复印机直接克隆文件内容(属性和状态);③ 可以在模板基础上修改(比如给复印件加批注),不影响原模板。原型模式就像游戏中的“对象复印机”,让对象创建更高效!
原型模式的核心公式
原型模式 = 原型对象(模板)+ clone()方法(克隆)+ 生产者(使用原型)
- 原型对象:已经创建好的、拥有完整属性和状态的对象(比如“15 血 3 速的幽灵”),作为克隆的模板;
- clone()方法:原型对象提供的“克隆接口”,返回一个与自身属性、状态完全一致(或可定制)的新对象;
- 生产者 :持有原型对象,通过调用 clone() 方法创建新对象,无需关心具体怪物类型。
核心价值:减少重复代码,解耦对象创建和具体类型——生产者不用知道要创建哪种怪物,只需持有对应的原型模板,通过克隆就能生成新对象。新增怪物时,无需修改生产者代码,只需创建新的原型对象即可。
二、怪物克隆案例:从零实现原型模式
关键字:clone()方法、深拷贝 / 浅拷贝、原型生产者、状态克隆
我们以“克隆不同属性的怪物”为例,一步步实现原型模式,解决传统方案的类爆炸问题!
1. 步骤 1:定义原型接口(添加 clone()方法)
首先修改怪物基类,添加纯虚函数 clone()——这是原型模式的核心,所有怪物子类都必须实现这个“克隆接口”。
// 怪物基类(原型接口)
class Monster {
public:
virtual ~Monster() {}
// 克隆方法:返回一个与当前对象完全一致的新怪物
virtual Monster* clone() const = 0;
// 辅助方法:显示怪物信息(方便测试)
virtual void showInfo() const = 0;
protected:
int health_; // 生命值
int speed_; // 移动速度
};
初学者须知:① clone()方法返回 Monster* 类型,确保所有子类的克隆对象都能统一处理;② 加 const 修饰,确保克隆时不修改原原型对象的状态;③ 子类必须实现 clone(),否则无法实例化(纯虚函数特性)。
2. 步骤 2:实现具体原型(怪物子类)
每个怪物子类实现 clone()方法,通过拷贝自身的属性(生命值、速度等)创建新对象。
// 幽灵怪物(具体原型)
class Ghost : public Monster {
public:
// 构造函数:初始化幽灵的属性
Ghost(int health, int speed) {
health_ = health;
speed_ = speed;
}
// 实现 clone():克隆一个新幽灵,属性与原对象一致
Monster* clone() const override {
// 直接调用自身构造函数,传入原对象的属性
return new Ghost(health_, speed_);
}
// 显示幽灵信息
void showInfo() const override {
std::cout << “👻 幽灵 – 生命值:” << health_ << “,速度:” << speed_ << std::endl; } }; // 恶魔怪物(具体原型)class Demon : public Monster {public: Demon(int health, int speed) {health_ = health; speed_ = speed;} Monster* clone() const override { return new Demon(health_, speed_); } void showInfo() const override { std::cout << “😈 恶魔 – 生命值:” << health_ << “,速度:” << speed_ << std::endl;} };
3. 步骤 3:实现原型生产者(通用 Spawner)
不再为每个怪物写单独的生产者,而是实现一个 通用生产者 ——持有一个原型对象,通过调用 clone() 方法创建新怪物。
// 原型生产者(通用版,支持所有怪物类型)
class Spawner {
public:
// 构造函数:传入原型对象(模板)
Spawner(Monster* prototype) : prototype_(prototype) {}
// 生产怪物:克隆原型对象
Monster* spawnMonster() {
if (prototype_) {
return prototype_->clone(); // 关键:调用原型的 clone()方法
}
return nullptr;
}
private:
Monster* prototype_; // 持有原型对象(模板)
};
设计亮点:① 通用性:一个 Spawner 类支持所有实现了 clone()的怪物,再也不用写 N 个生产者;② 灵活性:想生产哪种怪物,就传入哪种怪物的原型;③ 可定制:原型对象可以有不同的属性(比如“快速幽灵”“虚弱幽灵”),克隆后自动继承这些状态。
4. 步骤 4:使用原型模式(克隆怪物)
创建原型对象→传入生产者→调用 spawnMonster()克隆新对象,整个流程简洁高效!
int main() {
// 1. 创建原型对象(模板)
Monster* fastGhostPrototype = new Ghost(15, 5); // 快速幽灵:15 血 5 速
Monster* weakDemonPrototype = new Demon(20, 2); // 虚弱恶魔:20 血 2 速
// 2. 创建生产者,绑定对应的原型
Spawner* fastGhostSpawner = new Spawner(fastGhostPrototype);
Spawner* weakDemonSpawner = new Spawner(weakDemonPrototype);
// 3. 生产怪物:克隆原型
Monster* ghost1 = fastGhostSpawner->spawnMonster();
Monster* ghost2 = fastGhostSpawner->spawnMonster();
Monster* demon1 = weakDemonSpawner->spawnMonster();
// 4. 验证结果:克隆的怪物与原型属性一致
ghost1->showInfo(); // 输出:👻 幽灵 – 生命值:15,速度:5
ghost2->showInfo(); // 输出:👻 幽灵 – 生命值:15,速度:5
demon1->showInfo(); // 输出:😈 恶魔 – 生命值:20,速度:2
// 5. 释放资源(避免内存泄漏)
delete ghost1;
delete ghost2;
delete demon1;
delete fastGhostPrototype;
delete weakDemonPrototype;
delete fastGhostSpawner;
delete weakDemonSpawner;
return 0;
}
5. 关键细节:深拷贝 vs 浅拷贝
上面的例子中,怪物的属性是 int 类型(基本数据类型),克隆时直接拷贝值即可(浅拷贝)。但如果怪物有 指针类型的属性 (比如持有一把武器 Weapon*),浅拷贝会导致原对象和克隆对象共享同一个指针(修改一个会影响另一个),这时候需要用 深拷贝——克隆指针指向的对象。
// 武器类(指针属性示例)
class Weapon {
public:
Weapon(std::string name, int damage) : name_(name), damage_(damage) {}
// 武器的克隆方法(深拷贝需要)
Weapon* clone() const {
return new Weapon(name_, damage_);
}
std::string name_;
int damage_;
};
// 带武器的幽灵(需要深拷贝)
class GhostWithWeapon : public Monster {
public:
GhostWithWeapon(int health, int speed, Weapon* weapon) {
health_ = health;
speed_ = speed;
weapon_ = weapon; // 持有武器指针
}
// 深拷贝实现 clone():不仅拷贝自身属性,还要拷贝武器对象
Monster* clone() const override {
return new GhostWithWeapon(
health_,
speed_,
weapon_->clone() // 克隆武器(深拷贝)
);
}
void showInfo() const override {
std::cout << “👻 带武器的幽灵 – 生命值:” << health_ << “,速度:” << speed_ << “,武器:” << weapon_->name_ << “(伤害:” << weapon_->damage_ << “)” << std::endl; } private: Weapon* weapon_; // 指针属性 }; // 使用示例 int main() { Weapon* sword = new Weapon(“ 幽灵之刃 ”, 10); Monster* prototype = new GhostWithWeapon(15, 5, sword); Spawner* spawner = new Spawner(prototype); Monster* ghost1 = spawner->spawnMonster();
Monster* ghost2 = spawner->spawnMonster();
ghost1->showInfo(); // 输出:👻 带武器的幽灵 – 生命值:15,速度:5,武器:幽灵之刃(伤害:10)
ghost2->showInfo(); // 输出:👻 带武器的幽灵 – 生命值:15,速度:5,武器:幽灵之刃(伤害:10)
// 验证深拷贝:修改 ghost1 的武器伤害,不影响 ghost2
delete ghost1->weapon_; // 先释放原武器
ghost1->weapon_ = new Weapon(“ 强化幽灵之刃 ”, 15);
ghost1->showInfo(); // 输出:👻 带武器的幽灵 – 生命值:15,速度:5,武器:强化幽灵之刃(伤害:15)
ghost2->showInfo(); // 输出不变,深拷贝成功!
// 释放所有资源(略)
return 0;
}
初学者避坑:① 浅拷贝:只拷贝指针地址,不拷贝指针指向的对象,适用于基本数据类型和不需要独立修改的指针;② 深拷贝:拷贝指针指向的对象,创建新的独立对象,适用于需要修改的指针属性;③ 忘记深拷贝会导致“浅拷贝陷阱”——原对象和克隆对象互相干扰,甚至内存泄漏。
三、原型模式的扩展:其他实现方案与语言特性
关键字:生产函数、模板、动态语言、第一公民类型
原型模式的核心是“克隆模板”,但在不同语言、不同场景下,还有更灵活的实现方案。我们来看看这些扩展方案,帮你应对更多场景!
1. 方案 1:生产函数(替代原型类)
如果不想给每个怪物类写 clone()方法,可以用 生产函数——直接将创建对象的函数作为参数传给生产者,简化代码。
// 定义生产函数类型:返回 Monster* 的函数指针
typedef Monster* (*SpawnCallback)();
// 幽灵生产函数:创建幽灵对象
Monster* spawnFastGhost() {
return new Ghost(15, 5);
}
// 恶魔生产函数:创建恶魔对象
Monster* spawnWeakDemon() {
return new Demon(20, 2);
}
// 基于生产函数的生产者
class CallbackSpawner {
public:
CallbackSpawner(SpawnCallback callback) : callback_(callback) {}
Monster* spawnMonster() {
return callback_(); // 调用生产函数创建对象
}
private:
SpawnCallback callback_;
};
// 使用示例
int main() {
// 传入生产函数,创建生产者
CallbackSpawner* ghostSpawner = new CallbackSpawner(spawnFastGhost);
CallbackSpawner* demonSpawner = new CallbackSpawner(spawnWeakDemon);
Monster* ghost = ghostSpawner->spawnMonster();
ghost->showInfo(); // 输出:👻 幽灵 – 生命值:15,速度:5
// 释放资源(略)
return 0;
}
适用场景:① 怪物创建逻辑简单,不需要复用状态(比如所有幽灵都是 15 血 5 速);② 不想写 clone()方法,追求代码简洁;③ 缺点:无法像原型模式那样灵活定制模板状态(比如快速幽灵、慢速幽灵需要写两个生产函数)。
2. 方案 2:C++ 模板(类型参数化)
利用 C ++ 的模板特性,将生产者类参数化,直接指定要创建的怪物类型,无需生产函数或 clone()方法。
// 模板生产者:T 是怪物类型(Ghost、Demon 等)
template
class TemplateSpawner : public Spawner {
public:
// 生产怪物:直接 new T 类型的对象(要求 T 有默认构造函数)
Monster* spawnMonster() override {
return new T();
}
};
// 带参数的模板生产者(支持自定义属性)
template
class ParamTemplateSpawner : public Spawner {
public:
// 构造函数:传入怪物的初始化参数
ParamTemplateSpawner(int health, int speed) : health_(health), speed_(speed) {}
Monster* spawnMonster() override {
return new T(health_, speed_); // 传入参数创建对象
}
private:
int health_;
int speed_;
};
// 使用示例
int main() {
// 无参数模板:要求 Ghost 有默认构造函数
Spawner* ghostSpawner = new TemplateSpawner();
// 带参数模板:创建 15 血 5 速的幽灵
Spawner* fastGhostSpawner = new ParamTemplateSpawner(15, 5);
Monster* ghost = fastGhostSpawner->spawnMonster();
ghost->showInfo(); // 输出:👻 幽灵 – 生命值:15,速度:5
// 释放资源(略)
return 0;
}
初学者须知:① 模板生产者的优点是类型安全,编译时检查错误;② 缺点是灵活性不足——如果怪物的初始化参数不同(比如有的需要武器,有的不需要),模板无法统一处理;③ 适合怪物类型固定、初始化参数简单的场景。
3. 方案 3:动态语言的“第一公民类型”
在 JavaScript、Python 等动态语言中,“类”本身是可以传递的对象(第一公民类型),可以直接将类作为参数传给生产者,无需克隆或模板。
// JavaScript 示例:动态语言的原型模式简化版
class Ghost {
constructor(health, speed) {
this.health = health;
this.speed = speed;
}
showInfo() {
console.log(`👻 幽灵 - 生命值:${this.health},速度:${this.speed}`);
}
}
class Demon {
constructor(health, speed) {
this.health = health;
this.speed = speed;
}
showInfo() {
console.log(`😈 恶魔 – 生命值:${this.health},速度:${this.speed}`);
}
}
// 生产者:直接接收类作为参数
class DynamicSpawner {
constructor(MonsterClass, health, speed) {
this.MonsterClass = MonsterClass; // 存储类
this.health = health;
this.speed = speed;
}
spawnMonster() {
// 直接创建类的实例
return new this.MonsterClass(this.health, this.speed);
}
}
// 使用示例
const ghostSpawner = new DynamicSpawner(Ghost, 15, 5);
const demonSpawner = new DynamicSpawner(Demon, 20, 2);
const ghost = ghostSpawner.spawnMonster();
ghost.showInfo(); // 输出:👻 幽灵 – 生命值:15,速度:5
动态语言优势:① 代码极简:无需 clone()方法、模板或函数指针,直接传递类和参数;② 灵活性高:可以动态切换要创建的类,甚至在运行时修改参数;③ 本质:动态语言的“类作为第一公民”特性,简化了原型模式的实现,但其核心思想(复用模板创建对象)与原型模式一致。
四、深度分析:原型模式的适用场景与常见误区
关键字:适用场景、常见误区、与其他模式的区别、数据原型
1. 适用场景(新手必记)
- ✅ 需要创建大量属性相似的对象(比如游戏中的怪物、道具、NPC,大部分属性相同,只有少数差异);
- ✅ 对象创建逻辑复杂(比如需要初始化多个属性、调用多个接口),克隆比重新构造更高效;
- ✅ 避免类爆炸(有多种对象类型,每种类型都需要生产者,用原型模式统一生产者);
- ✅ 运行时动态创建对象(比如根据配置文件加载原型,克隆生成不同的对象);
- ✅ 需要复用对象状态(比如“强化版武器”是“普通武器”的克隆,只修改伤害属性)。
2. 常见误区(新手避坑)
- ❌ 滥用原型模式:如果对象创建逻辑简单(只有 1 - 2 个属性),直接用构造函数更简洁,无需多此一举写 clone();
- ❌ 忽略深拷贝 / 浅拷贝:对象有指针、引用类型的属性时,忘记深拷贝,导致原对象和克隆对象互相干扰;
- ❌ 原型对象状态不稳定:克隆后修改原型对象的状态,会影响后续的克隆对象(如果需要原型状态不变,克隆时要确保深拷贝);
- ❌ 与单例模式冲突:单例对象不能被克隆(否则会破坏单例的唯一性),避免给单例类实现 clone();
- ❌ 过度设计原型层次 :原型模式不需要复杂的继承层次,每个具体原型直接实现 clone() 即可,无需额外抽象。
3. 与其他模式的区别(避免混淆)
两者都用于创建对象,但核心思路不同:
① 原型模式:基于“克隆已有对象”,对象的属性和状态来自原型模板,适合对象属性相似的场景;
② 工厂方法模式:基于“定义创建接口,子类实现”,对象的属性和状态在工厂中硬编码,适合对象类型不同、创建逻辑差异大的场景。
例子:① 生产 100 个“15 血 5 速的幽灵”——用原型模式(克隆 1 个原型);② 生产幽灵、恶魔、巫师(类型不同,创建逻辑不同)——用工厂方法模式。
两者都关注对象创建,但侧重点不同:
① 原型模式:快速克隆已有对象,强调“复用已有状态”,无需关心对象的构建步骤;
② 建造者模式:分步构建复杂对象,强调“定制化构建过程”,可以灵活组合不同的属性和部件。
例子:① 克隆一个已有的“全套装备的玩家”——用原型模式;② 从零开始构建“穿头盔 + 持武器 + 带药水的玩家”——用建造者模式。
4. 高级应用:数据原型(游戏配置文件复用)
现代游戏中,大量内容(怪物、道具、技能)都通过配置文件(JSON、XML)定义。如果每个配置都重复写相同的属性(比如所有哥布林都有相同的生命值、抗性),会导致配置冗余。这时可以用“数据原型”——在配置中引用其他配置作为原型,实现属性复用。
// 原始配置(冗余):每个哥布林都重复写相同的属性
{
"goblin_grunt": {// 哥布林步兵
"name": "哥布林步兵",
"minHealth": 20,
"maxHealth": 30,
"resists": ["cold", "poison"],
"weaknesses": ["fire", "light"]
},
"goblin_wizard": {// 哥布林巫师
"name": "哥布林巫师",
"minHealth": 20, // 重复属性
"maxHealth": 30, // 重复属性
"resists": ["cold", "poison"], // 重复属性
"weaknesses": ["fire", "light"], // 重复属性
"spells": ["fire_ball", "lightning_bolt"] // 独有属性
},
"goblin_archer": {// 哥布林弓箭手
"name": "哥布林弓箭手",
"minHealth": 20, // 重复属性
"maxHealth": 30, // 重复属性
"resists": ["cold", "poison"], // 重复属性
"weaknesses": ["fire", "light"], // 重复属性
"attacks": ["short_bow"] // 独有属性
}
}
// 数据原型优化(无冗余):引用原型复用属性
{
“goblin_grunt”: {// 基础原型:哥布林步兵
“name”: “ 哥布林步兵 ”,
“minHealth”: 20,
“maxHealth”: 30,
“resists”: [“cold”, “poison”],
“weaknesses”: [“fire”, “light”]
},
“goblin_wizard”: {// 引用原型,只写独有属性
“name”: “ 哥布林巫师 ”,
“prototype”: “goblin_grunt”, // 原型:哥布林步兵
“spells”: [“fire_ball”, “lightning_bolt”]
},
“goblin_archer”: {// 引用原型,只写独有属性
“name”: “ 哥布林弓箭手 ”,
“prototype”: “goblin_grunt”, // 原型:哥布林步兵
“attacks”: [“short_bow”]
}
}
游戏引擎中的数据原型解析逻辑
游戏加载配置时,需要实现“原型委托”逻辑——当读取一个配置时,如果它有 ”prototype” 字段,就先加载原型配置的属性,再用当前配置的属性覆盖(或补充):
// 伪代码:解析数据原型的逻辑
function loadEntityConfig(configName) {
const config = readJson(`config/${configName}.json`);
// 如果有原型,递归加载原型配置
if (config.prototype) {
const prototypeConfig = loadEntityConfig(config.prototype);
// 合并配置:原型属性 + 当前配置属性(当前属性覆盖原型)
return {...prototypeConfig, ...config};
}
return config;
}
// 使用示例
const wizardConfig = loadEntityConfig(“goblin_wizard”);
console.log(wizardConfig);
// 输出:{
// “name”: “ 哥布林巫师 ”,
// “minHealth”: 20, // 来自原型
// “maxHealth”: 30, // 来自原型
// “resists”: [“cold”, “poison”], // 来自原型
// “weaknesses”: [“fire”, “light”], // 来自原型
// “spells”: [“fire_ball”, “lightning_bolt”] // 独有属性
// }
优势:① 配置无冗余,减少文件大小和维护成本;② 便于批量修改,比如要让所有哥布林的生命值 +10,只需修改原型配置;③ 支持多层原型,比如“哥布林精英巫师”可以引用“哥布林巫师”作为原型,再添加新属性。
五、现实复杂场景:原型模式的实战应用
关键字:对象池、动态生成、配置复用、状态保存
1. 场景 1:对象池(复用频繁创建 / 销毁的对象)
游戏中,子弹、敌人、粒子效果等对象会频繁创建和销毁,导致内存碎片和性能损耗。用“对象池 + 原型模式”可以优化:① 初始化时创建一批原型对象,存入对象池;② 需要时从池中取出对象,克隆后使用;③ 用完后不销毁,放回池中供下次使用。
// 子弹原型(模板)
class Bullet : public Monster {
public:
Bullet(int speed, int damage) {
speed_ = speed;
damage_ = damage;
}
Monster* clone() const override {
return new Bullet(speed_, damage_);
}
private:
int damage_;
};
// 子弹对象池
class BulletPool {
public:
// 初始化对象池:创建一批原型子弹
BulletPool(int count) {
for (int i = 0; i < count; i++) {pool_.push_back(new Bullet(10, 5)); // 原型:速度 10,伤害 5 } } // 从池中获取子弹(克隆原型)Bullet* getBullet() { if (pool_.empty()) {// 池为空时,克隆一个新原型 return new Bullet(10, 5); } Bullet* bullet = dynamic_cast<Bullet*>(pool_.back()->clone());
pool_.pop_back();
return bullet;
}
// 回收子弹(放回池中)
void recycleBullet(Bullet* bullet) {
pool_.push_back(bullet);
}
private:
std::vector<Monster*> pool_;
};
// 使用示例
int main() {
BulletPool pool(10); // 初始化 10 个原型子弹的对象池
// 游戏循环中获取子弹
for (int i = 0; i < 15; i++) {Bullet* bullet = pool.getBullet(); std::cout << “ 发射子弹:速度 ” << bullet->speed_ << “,伤害 ” << bullet->damage_ << std::endl; // 用完后回收 pool.recycleBullet(bullet); } // 释放资源(略)return 0; }
2. 场景 2:动态生成关卡(根据原型随机克隆)
procedural 生成(procedural generation)关卡时,需要随机生成大量怪物、道具。用原型模式可以:① 定义多种基础原型(比如“普通怪物”“精英怪物”“普通道具”);② 随机选择原型,克隆后修改部分属性(比如生命值、伤害),生成多样化的关卡内容。
// 定义基础原型
Monster* normalMonsterProto = new Ghost(20, 3); // 普通怪物原型
Monster* eliteMonsterProto = new Demon(50, 6); // 精英怪物原型
Item* healthPotionProto = new Item("生命药水", 50); // 生命药水原型
// 随机生成怪物
Monster* spawnRandomMonster() {
int randType = rand() % 2; // 0= 普通,1= 精英
Monster* prototype = (randType == 0) ? normalMonsterProto : eliteMonsterProto;
// 克隆原型,并随机修改属性(±20%)
Monster* monster = prototype->clone();
monster->health_ *= (0.8 + rand() % 40 / 100.0); // 生命值 80%-120%
monster->speed_ *= (0.8 + rand() % 40 / 100.0); // 速度 80%-120%
return monster;
}
// 生成关卡
void generateLevel(int monsterCount) {
std::cout << “ 生成关卡,包含 ” << monsterCount << “ 个怪物:” << std::endl; for (int i = 0; i < monsterCount; i++) {Monster* monster = spawnRandomMonster(); monster->showInfo();
delete monster;
}
}
3. 场景 3:状态保存与回滚(克隆对象状态)
游戏中,玩家升级、使用道具前,需要保存当前状态(生命值、装备、技能),以便后续回滚(比如升级失败、道具使用错误)。用原型模式可以:① 保存状态时,克隆当前玩家对象作为“快照”;② 回滚时,用快照克隆一个新对象,覆盖当前玩家状态。
// 玩家类(支持克隆)
class Player : public Monster {
public:
Player(int health, int level, std::vector equipment) {
health_ = health;
level_ = level;
equipment_ = equipment;
}
Monster* clone() const override {
return new Player(health_, level_, equipment_);
}
// 升级(可能失败)
bool levelUp() {
// 保存状态快照
Monster* snapshot = clone();
std::cout << “ 尝试升级 … 当前等级:” << level_ << std::endl; // 模拟升级失败(50% 概率)if (rand() % 2 == 0) {level_++; health_ += 50; std::cout << “ 升级成功!等级:” << level_ << “,生命值:” << health_ << std::endl; delete snapshot; return true;} else {// 升级失败,回滚状态 Player* rollbackPlayer = dynamic_cast<Player*>(snapshot);
health_ = rollbackPlayer->health_;
level_ = rollbackPlayer->level_;
equipment_ = rollbackPlayer->equipment_;
std::cout << “ 升级失败!回滚到等级:” << level_ << std::endl; delete snapshot; return false; } } private: int level_; std::vector equipment_;
};
// 使用示例
int main() {
Player* player = new Player(100, 1, {“ 新手剑 ”, “ 布衣 ”});
player->levelUp(); // 50% 概率成功
player->levelUp(); // 50% 概率成功
delete player;
return 0;
}
终极总结:原型模式是游戏开发中“高效创建对象”的核心模式,核心优势是“复用已有状态,减少重复代码”。初学者要掌握三个关键点:① 实现 clone()方法,区分深拷贝和浅拷贝;② 用通用生产者替代多个专用生产者,避免类爆炸;③ 灵活运用扩展方案(生产函数、模板、数据原型)应对不同场景。记住:原型模式不是万能的,只有当对象创建复杂、属性相似时,它才能发挥最大价值!
“`