原型模式
定义:
原型模式是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
在游戏中我们会遇到各种各样的怪物,我们为了创建各种不同的怪物,可以简单粗暴的为每个怪物类设计一个创建类,关系如下:
1.粗暴方式:
class Monster //怪物基类
{
// Stuff...
};
class Ghost : public Monster {};
class Demon : public Monster {};
class Sorcerer : public Monster {};
class Spawner //怪物创建器基类
{
public:
virtual ~Spawner() {}
virtual Monster* spawnMonster() = 0;
};
class GhostSpawner : public Spawner
{
public:
virtual Monster* spawnMonster()
{
return new Ghost();
}
};
class DemonSpawner : public Spawner
{
public:
virtual Monster* spawnMonster()
{
return new Demon();
}
};
但这种方式显然会引入很多冗余的类,而且代码有很多重复,不便维护,我们可以通过原型模式进行改进。可以让每个怪物类自己设定一个clone方法进行自我拷贝,然后我们仅需要创建一个创建类便可完成工作,代码如下:
2.原型模式
class Monster
{
public:
virtual ~Monster() {}
virtual Monster* clone() = 0; //进行自我复制
// Other stuff...
};
class Ghost : public Monster {
public:
Ghost(int health, int speed)
: health_(health),
speed_(speed)
{}
virtual Monster* clone()
{
return new Ghost(health_, speed_);
}
private:
int health_;
int speed_;
};
class Spawner //只需要一个创建类便可完成
{
public:
Spawner(Monster* prototype)
: prototype_(prototype)
{}
Monster* spawnMonster()
{
return prototype_->clone();
}
private:
Monster* prototype_;
};
//使用如下
Monster* ghostPrototype = new Ghost(15, 3);
Spawner* ghostSpawner = new Spawner(ghostPrototype);
通过在创建类中内嵌一个原型来创建相应的副本。
此外我们还可以通过创建函数来实现:
3.函数实现
Monster* spawnGhost() //创建函数
{
return new Ghost();
}
typedef Monster* (*SpawnCallback)();
class Spawner
{
public:
Spawner(SpawnCallback spawn)
: spawn_(spawn)
{}
Monster* spawnMonster()
{
return spawn_();
}
private:
SpawnCallback spawn_; //函数指针
};
//使用如下
Spawner* ghostSpawner = new Spawner(spawnGhost);
4.C++模板实现
class Spawner
{
public:
virtual ~Spawner() {}
virtual Monster* spawnMonster() = 0;
};
template <class T>
class SpawnerFor : public Spawner
{
public:
virtual Monster* spawnMonster() { return new T(); }
};
//使用方式
Spawner* ghostSpawner = new SpawnerFor<Ghost>();
原型模式的优点
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率。
- 可以动态增加或减少产品类。
- 原型模式提供了简化的创建结构。
- 可以使用深克隆的方式保存对象的状态。
原型模式的缺点
- 需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码。