简介
和原型模式相关的操作是“克隆”。更进一步地,原型模式并不是为了解决类的复制操作问题,而是为了将克隆操作放在接口类上,让其它类来使用这一接口,以此达到代码复用的目的。
在一些直接创建对象开销较大的情况下,我们也可以先返回某个类的克隆对象作为缓存,以此降低整体的开销。
例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。——菜鸟教程
原型
现在假设我们制作一个游戏地图,该地图能够存储实体的指针。我们可以对实体增加“克隆”操作,就能在地图中低开销地快速新增大量实体。在这个过程中,我们将实体作为一个带有“克隆”操作的抽象类Entity,它就是原型模式中的“原型”。
1
2
3
4
5
6
7
8
9
10
11
12
| class Entity {
public:
explicit Entity() = default;
virtual ~Entity();
virtual Entity* clone() const = 0;
int id() const;
protected:
int _id = 0;
};
int Entity::id() const{return _id;}
Entity::~Entity() = default;
|
我们再继承原型创建其它具体的实体类,并实现具体的clone()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
| //Enemy
class Enemy : public Entity {
public:
Enemy();
Entity* clone() const override;
};
Enemy::Enemy() : Entity() {
_id = 1;
}
Entity* Enemy::clone() const{
std::cout << "An Enemy was cloned." << std::endl;
return new Enemy();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| //Chest
class Chest : public Entity {
public:
explicit Chest();
Entity* clone() const override;
};
Chest::Chest() : Entity(){
_id = 2;
}
Entity* Chest::clone() const{
std::cout << "A Chest was cloned." << std::endl;
return new Chest();
}
|
调用原型
接下来我们实现存储Entity*的Map对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class Map {
public:
explicit Map(Vector2D size);
~Map();
void printMap() const;
void addEntity(Entity*, Vector2D pos);
private:
std::vector<std::vector<Entity*>> _map;
//2D dynamic array
};
Map::Map(const Vector2D size) :_map(size.x, std::vector<Entity*>(size.y,nullptr)){}
Map::~Map() {/*......*/}
void Map::printMap() const {/*......*/}
void Map::addEntity(Entity* entity, const Vector2D pos) {
if(pos.y < _map.size() && pos.x < _map[0].size())
_map[pos.x][pos.y] = entity;
}
|
我们使用一个可变数组直接存储了Entity*变量,这大大提高了代码复用型。
在主函数中,我们可以创建不同的Entity*对象并将其大量克隆到地图中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| int main() {
auto *map = new Map(Vector2D(5,5));
map->printMap();
std::cout << "-------------------" << std::endl;
auto enemy = new Enemy();
for (int i = 0; i < 3; i++) {
map->addEntity(enemy->clone(), Vector2D(randInRange(2,4),randInRange(2,4)));
}
map->printMap();
std::cout << "-------------------" << std::endl;
auto chest = new Chest();
for (int i = 0; i < 2; i++) {
map->addEntity(chest->clone(), Vector2D(randInRange(0,2),randInRange(0,2)));
}
map->printMap();
return 0;
}
|
运行结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| 0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
-------------------
An Enemy was cloned.
An Enemy was cloned.
An Enemy was cloned.
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 1
0 0 1 0 1
-------------------
A Chest was cloned.
A Chest was cloned.
-------------------
0 0 0 0 0
0 0 2 0 0
2 0 0 0 0
0 0 0 0 1
0 0 1 0 1
|
总结
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。
在实际场景中,克隆可能会在父类和子类之间进行,并且可能是动态的。我们给父类提供一个虚克隆函数,就能通过使用父类指针来实现子类对象的拷贝。这本质上也是面向对象中多态的体现。