介绍
设计模式分为创建型模式、结构型模式和行为模式。创建型模式主要是在创建对象的时候使用的,接下来以植物大战僵尸作为例子。
工厂模式
简单工厂模式
僵尸类是所有僵尸的基类,僵尸类派生了普通僵尸类、路障僵尸类。
僵尸工厂由一个静态函数或者全局函数组成,通过对函数传参不同来创建不同种类的僵尸。这里通过传入字符串
classDiagram
class Zombie{
<<Interface>>
-Hp
-Speed
+move()
+eat()
}
Zombie <|.. NormalZombie
class NormalZombie{
-Hp
-Speed
+move()
+eat()
}
Zombie <|.. RoadblockZombie
class RoadblockZombie{
-Hp
-Speed
+move()
+eat()
}
%%ZombieFactory --> NormalZombie
%%ZombieFactory --> RoadblockZombie
class ZombieFactory{
+createZombie(ZombieType) Zombie$
}
下面是C++代码
class Zombie{ // 僵尸基类
private:
int Hp;
int Speed;
public:
Zombie(int hp_, int speed_): Hp(hp_), Speed(speed_){}
virtual void move() = 0;
virtual void eat() = 0;
}
class NormalZombie: public Zombie{ // 普通僵尸
NormalZombie(int hp_, int speed_): Zombie(hp_, speed_){}
}
class RoadblockZombie: public Zombie{ // 路障僵尸
RoadblockZombie(int hp_, int speed_): Zombie(hp_, speed_){}
}
class ZombieFactory{ // 僵尸工厂
public:
// 通过静态方法来创建僵尸,必须返回指针,因为需要满足隐式转换
static Zombie* createZombie(string ZombieType){
if(ZombieType == "NormalZombie"){
return new NormalZombie(100, 10);
}else if(ZombieType == "RoadblockZombie"){
return new RoadblockZombie(200, 10);
}else{
throw "ZombieType error!";
}
}
}
auto normalZombie = ZombieFactory::createZombie("NormalZombie");
auto roadblockZombie = ZombieFactory::createZombie("RoadblockZombie");
工厂方法模式
简单工厂中,对于工厂实现就一个函数,还是需要通过传入不同参数来实现。而且有可能传参容易传错,导致检查都不好检查,因此不雅观。
僵尸类、普通僵尸类和具体僵尸类都跟简单工厂一样。实现一个僵尸工厂类,他是一个接口,其派生了普通僵尸工厂和路障僵尸工厂。普通僵尸工厂只生产普通僵尸,路障僵尸工厂只生产路障僵尸。
这样子很雅观。
classDiagram
class Zombie{
<<Interface>>
-Hp
-Speed
+move()
+eat()
}
Zombie <|.. NormalZombie
class NormalZombie{
-Hp
-Speed
+move()
+eat()
}
Zombie <|.. RoadblockZombie
class RoadblockZombie{
-Hp
-Speed
+move()
+eat()
}
%%Zombie <-- ZombieFactory
class ZombieFactory{
<<Interface>>
+createZombie() Zombie$
}
ZombieFactory <|.. NormalZombieFactory
%%NormalZombie<--NormalZombieFactory
class NormalZombieFactory{
+createZombie() NormalZombie$
}
ZombieFactory <|.. RoadblockZombieFactory
%%RoadblockZombie<--RoadblockZombieFactory
class RoadblockZombieFactory{
+createZombie() RoadblockZombie$
}
下面是C++代码:
class Zombie{ // 僵尸基类
private:
int Hp;
int Speed;
public:
Zombie(int hp_, int speed_): Hp(hp_), Speed(speed_){}
virtual void move() = 0;
virtual void eat() = 0;
}
class ZombieFactory{ // 僵尸工厂
public:
// 通过静态方法来创建僵尸
virtual static Zombie* createZombie() = 0; // 使用指针,必须能够实现隐士转换才可,C++语法要求
}
class NormalZombie: public Zombie{ // 普通僵尸
NormalZombie(int hp_, int speed_): Zombie(hp_, speed_){}
}
class RoadblockZombie: public Zombie{ // 路障僵尸
RoadblockZombie(int hp_, int speed_): Zombie(hp_, speed_){}
}
class NormalZombieFactory: public ZombieFactory{ // 路障僵尸工厂,生产路障僵尸
public:
// 通过静态方法来创建僵尸
static NormalZombie* createZombie() override {
return new NormalZombie(100, 10);
}
}
class RoadblockZombieFactory: public ZombieFactory{ // 路障僵尸工厂,生产路障僵尸
public:
// 通过静态方法来创建僵尸
static RoadblockZombie* createZombie() override {
return new RoadblockZombie(200, 10);
}
}
auto normalZombie = NormalZombieFactory::createZombie();
auto roadblockZombie = RoadblockZombieFactory::createZombie();
抽象工厂模式
抽象工厂其实是工厂方法模式的一个扩展,目前只有一种具体的实体,即僵尸。但是在真正一把游戏中,其实还有植物,阳光,子弹等等,他们也都是类。
假设额外实现一个植物类,再派生单发射手和双发射手。再实现一个游戏工厂,有两个成员变量(一个是抽象植物工厂类,一个是抽象僵尸工厂类)
接下来实现僵尸类及其工厂(工厂方法模式),植物类及其工厂(工厂方法模式)以及一个游戏类和游戏工厂。
- 僵尸类及僵尸工厂(参考工厂方法模式)
classDiagram
class Zombie{
<<Interface>>
-Hp
-Speed
+move()
+eat()
}
Zombie <|.. NormalZombie
class NormalZombie{
-Hp
-Speed
+move()
+eat()
}
Zombie <|.. RoadblockZombie
class RoadblockZombie{
-Hp
-Speed
+move()
+eat()
}
class ZombieFactory{
<<Interface>>
+create() Zombie
}
ZombieFactory <|.. NormalZombieFactory
%%NormalZombie<--NormalZombieFactory
class NormalZombieFactory{
+create() NormalZombie
}
ZombieFactory <|.. RoadblockZombieFactory
%%RoadblockZombie<--RoadblockZombieFactory
class RoadblockZombieFactory{
+create() RoadblockZombie
}
```cpp
class Zombie{ // 僵尸基类
private:
int Hp;
int Speed;
public:
Zombie(int hp_, int speed_): Hp(hp_), Speed(speed_){}
virtual void move() = 0;
virtual void eat() = 0;
}
class ZombieFactory{ // 僵尸工厂
public:
// 通过静态方法来创建僵尸
virtual Zombie* create() = 0; // 使用指针,必须能够实现隐士转换才可,C++语法要求
}
class NormalZombie: public Zombie{ // 普通僵尸
NormalZombie(int hp_, int speed_): Zombie(hp_, speed_){}
}
class RoadblockZombie: public Zombie{ // 路障僵尸
RoadblockZombie(int hp_, int speed_): Zombie(hp_, speed_){}
}
class NormalZombieFactory: public ZombieFactory{ // 路障僵尸工厂,生产路障僵尸
public:
// 通过静态方法来创建僵尸
NormalZombie* create() override {
return new NormalZombie(100, 10);
}
}
class RoadblockZombieFactory: public ZombieFactory{ // 路障僵尸工厂,生产路障僵尸
public:
// 通过静态方法来创建僵尸
RoadblockZombie* create() override {
return new RoadblockZombie(200, 10);
}
}
- 植物类及植物工厂(同僵尸类及僵尸工厂)
classDiagram
class Plant{
<<Interface>>
-Hp
-Sun
+attack()
}
Plant<|.. Peashooter
class Peashooter{
-Hp
-Sun
+attack()
}
Plant<|.. Doubleshooter
class Doubleshooter{
-Hp
-Sun
+attack()
}
class PlantFactory{
<<Interface>>
+create() Plant
}
PlantFactory <|.. PeashooterFactory
%%NormalZombie<--NormalZombieFactory
class PeashooterFactory{
+create() PeashooterFactory
}
PlantFactory <|.. DoubleshooterFactory
%%RoadblockZombie<--RoadblockZombieFactory
class DoubleshooterFactory{
+create() DoubleshooterFactory
}
```cpp
class Plant{ // 植物基类
private:
int Hp;
int Sun;
public:
Plant(int hp_, int sun_): Hp(hp_), Sun(sun_){}
virtual attack() = 0;
}
class PlantFactory{ // 僵尸工厂
public:
// 通过静态方法来创建僵尸
virtual Plant* create() = 0; // 使用指针,必须能够实现隐士转换才可,C++语法要求
}
class Peashooter: public Plant{
Peashooter(int hp_, int sun_): Plant(hp_, sun_){}
}
class Doubleshooter: public Plant{
Doubleshooter(int hp_, int sun_): Plant(hp_, sun_){}
}
class Peashooter: public PlantFactory{ // 路障僵尸工厂,生产路障僵尸
public:
// 通过静态方法来创建僵尸
Peashooter* create() override {
return new Peashooter(150, 200);
}
}
class Doubleshooter: public PlantFactory{ // 路障僵尸工厂,生产路障僵尸
public:
// 通过静态方法来创建僵尸
Doubleshooter* create() override {
return new Doubleshooter(100, 100);
}
}
- 抽象游戏类及具体游戏,抽象游戏中包括了僵尸工厂和植物工厂。然后创建了一个游戏工厂,游戏工厂用来创建游戏,其实也是用来创建一系列工厂的。然后实现了简单游戏工厂和困难游戏工厂。
classDiagram
class Game{
%% <<Interface>>
+PlantFactory
+ZombieFactory
}
%%Game <|.. EasyGame
%%class EasyGame{
%% +ZombieFactory = NormalZombieFactory
%% +PlantFactory = DoubleshooterFactory
%%}
%%Game <|.. HardGame
%%class HardGame{
%% +ZombieFactory = RoadblockZombieFactory
%% +PlantFactory = PeashooterFactory
%%}
class GameFactory{
<<Interface>>
+createGame() Game$
}
GameFactory <|.. EasyGameFactory
class EasyGameFactory{
+createGame() Game$
}
GameFactory <|.. HardGameFactory
class HardGameFactory{
+createGame() Game$
}
```cpp
class Game{ // 游戏工厂
public:
// 通过静态方法来创建僵尸
Zombie* ZombieFactory = nullptr; // 使用指针,必须能够实现隐式转换才可,C++语法要求
Plant* PlantFactory = nullptr; // 使用指针,必须能够实现隐式转换才可,C++语法要求
Game(Zombie* zf, Plant* pf):ZombieFactory(zf), PlantFactory(pf){}
}
/*
// 简单游戏
class EasyGame: public Game{
EasyGame(Zombie* zf, Plant* pf): Game(zf, pf){}
}
// 困难游戏
class HardGame: public Game{
HardGame(Zombie* zf, Plant* pf): Game(zf, pf){}
HardGameFactory(){
this->ZombieFactory = new RoadblockZombieFactory(); // 具体实现不同的工厂
this->PlantFactory = new PeashooterFactory(); // 具体实现不同的工厂
}
}
*/
// 创建游戏的工厂,游戏工厂
class GameFactory{
public:
virtual static Game* createGame() = 0;
}
// 专门创建简单模式的游戏工厂,简单游戏工厂
class EasyGameFactory: public GameFactory{
public:
static Game* createGame() {
return new Game(
new NormalZombieFactory(),
new DoubleshooterFactory()
);
};
}
// 专门创建困难模式的游戏工厂,困难游戏工厂
class HardGameFactory: public GameFactory{
public:
static Game* createGame() {
return new Game(
new RoadblockZombieFactory(),
new PeashooterFactory()
);
};
}
综合上面,就可以简单创建许多游戏了:
// 1. 简单模式下
auto game = EasyGameFactory::createGame();
Zombie* easyGameZombie = game->ZombieFactory->create();
Plant* easyGamePlant = game->PlantFactory->create();
// 2. 困难模式下
auto game = HardGameFactory::createGame(); // 只有这一行不一样,所以可以直接把game传入函数,这样就很方便创建不同难度的游戏了
Zombie* easyGameZombie = game->ZombieFactory->create();
Plant* easyGamePlant = game->PlantFactory->create();
总结
先介绍一个名词:
- 抽象产品 (Abstract Product) :若干个不同实体有相同的特征,这些特征组成的一个抽象类/接口,比如僵尸类可以看作一个抽象类。
- 具体产品 (Concrete Product):抽象产品的一个具体实现,比如路障僵尸就是一个具体产品类。
- 抽象工厂 (Abstract Factory) :用于创造产品的,但是它只能返回抽象产品类,其派生的子类才会返回具体产品。比如僵尸工厂类。
- 具体工厂 (Concrete Factory) :抽象工程的一个具体实现,比如路障僵尸工厂类。
- 聚合产品:里面有若干个抽象工厂指针,用于实现多态
- 聚合抽象工厂:用于生产聚合产品的抽象工厂
- 聚合具体工厂:用于生产聚合产品的具体工厂,通过改变聚合产品中的指针来实现。
然后介绍三种模式:
- 简单工厂模式:实现抽象产品和具体产品,然后实现一个工厂类(含有静态函数)或者直接实现一个函数,该函数左右时返回抽象产品指针,该函数可以通过传入不同参数来实现创建不同的具体产品。
- 工厂方法模式:实现抽象产品和具体产品,实现抽象工厂和具体工厂。抽象工厂定义了一些规定(比如创建产品),具体工厂实现这些规定时可以采用不同的方法,以达到实现创建不同产品的目的。
- 抽象工厂模式:生产工厂的工厂,实现抽象产品和具体产品以及对应的抽象工厂和具体工厂,实现聚合产品以及对应的抽象工厂和具体工厂。
生成器/创建者模式
定义一个实体创建者基类,它定义了一些接口,比如设置血量,加载音乐,加载图片,设定动画间隔等等,但是一定要包括一个返回实体函数,这个返回实体函数用于返回真正的实体。对于植物和僵尸其实这些都是通用的,但是僵尸和植物也会有一些不同点。因此,再由实体创建者基类派生出僵尸创建者类和植物创建者类。
- 创造者基类与其派生类(植物实体创建者,僵尸实体创建者),这里两个实体只是举了个例子而已。
classDiagram
class Entity{
+hp
+musicPath
+interval
}
Entity<|..PlantEntity
class PlantEntity
Entity<|..ZombieEntity
class ZombieEntity
class Builder{
+getSubstance() Entity
+setHP(hp)
+loadMusic(Path):Music
+setInterval(interval)
}
Builder<|.. PlantBuilder
class PlantBuilder{
+getSubstance() PlantEntity
+setHP(hp)
+loadMusic(Path):Music
+setInterval(interval)
}
Builder<|.. ZombieBuilder
class ZombieBuilder{
+getSubstance() ZombieEntity
+setHP(hp)
+loadMusic(Path):Music
+setInterval(interval)
}
```cpp
struct Entity{
int hp;
string musicPath;
float interval;
}
struct PlantEntity: public Entity{}
struct ZombieEntity: public Entity{}
class Builder{
Entity *entity = nullptr; // 也可以选择在 setHP 等函数参数中传入示例(设置他们为静态成员变量即可),而不是用这种类成员的示例。
public:
virtual Entity* getSubstance() = 0; // 也可以实现一些逻辑,也可以设置成纯虚函数
virtual void setHP(int hp){entity->hp = hp;}
virtual void loadMusic(string path){entity->musicPath = path;}
virtual void setInterval(float interval) {entity->interval = interval;}
}
class PlantBuilder : public Builder{
public:
PlantEntity* getSubstance() override {
this->entity = new PlantEntity();
setHP(100);
loadMusic("./Plant/music/1.mp3");
setInterval(1.0);
return this->entity;
}
}
class ZombieBuilder : public Builder{
public:
ZombieEntity* getSubstance() override {
this->entity = new ZombieEntity();
setHP(100);
loadMusic("./Plant/music/1.mp3");
setInterval(1.0);
return this->entity;
}
}
- 创建主管,主管中可以创建植物实体或者僵尸实体,并且能够返回实体。当然,这个主管类可以多保存一会,而不是直接把实体返回,这里采用简单的方法而已
classDiagram
class Director{
+makePlantEntity(builder)PlantEntity
+makeZombieEntity(builder)ZombieEntity
}
```cpp
class Director{
public:
PlantEntity* makePlantEntity(Builder* builder){
return builder->getSubstance();
}
ZombieEntity* makeZombieEntity(Builder* builder){
return builder->getSubstance();
}
}
使用
auto director = new Director();
auto builder = new PlantBuilder(); // 植物创建者
auto entity = director->makePlantEntity(builder);
生成器的核心在于,创建产品的时候,不同产品是设计非常复杂的步骤的。所以对于产品的创建专门定义了一个基类和若干个具体实现类,然后交给主管类(Director)进行实际创建,使用者不需要管具体实现是怎么样的。甚至实体可以放在主管类里面,而不是由用户来管理等。
原型模式
原型模式非常简单,就是克隆模式。对于一个普通僵尸来说,创建它需要加载动画和音频,这些是从磁盘操作非常耗时,而且所有普通僵尸的动画和音频是一样的,那么其实完全可以从内存拷贝音频和动画数据,这样会节省时间。
classDiagram
class Zombie{
+clone()Zombie
}
Zombie<|..NormalZombie
class NormalZombie{
+Conductor()
+clone()NormalZombie
}
```cpp
class Zombie{
virtual Zombie* clone() = 0;
};
class NormalZombie{
private:
Music music;
Gif gif;
public:
NormalZombie() = default;
NormalZombie(string music_path, string gif_path){
music = loadMusic(music_path);
gif = loadGIF(music_path);
}
NormalZombie* clone(){
auto zombie = new NormalZombie();
zombie->music = this->music;
zombie->gif = this->gif;
return zombie;
}
};
原型模式适用于,在创造对象的时候,需要花费的时间比克隆对象要长,而且每个对象其实是一样的。
单例模式
对于一整局游戏来说,游戏的一些参数是全局的,比如阳光,车数量等等。但是这些参数又会被僵尸、植物所更改。对于这些参数,又不希望采用一个个全局变量管理,而是希望采用统一的类管理,就需要单例模式。
classDiagram
class Information{
-information
-sun
-car
+getInstance()Information
addSun(sun_)
reduceSun(sun_)
reduceCar()
}
```cpp
class Information{
private:
static Information* info = nullptr;
Information() = default; // 不允许外部访问构造方法,这样其他地方就无法构造示例了
Information& (const Information&) = default; // 隐藏复制等等
int sun;
int car;
public:
static Information* getInstance(){
if(not info) info = new Information();
return info;
}
void addSun(int sun_){
if(sun + sun_ < 9999) sun += sun_;
}
void reduceSun(int sun_){
if(sun >= sun_) sun -= sun_;
}
void reduceCar(){
if(car > 0) --car;
}
};
单例模式的核心在于,整个工程只会有一个实例,并且这个实例可以被其他地方获取,而且所有通过这个类获取的实例都是同一个,但是需要手动保证线程安全。
实例分为饿汉子和懒汉子模式,饿汉子就是一开始就创建实例,这样会导致工程启动很慢。懒汉子模式就是,在使用的时候创建,但是会在第一次使用的时候卡一下。
- 饿汉子模式
class Information{
private:
// static Information* info = new Information();// 指针管理
static Information info; // 直接使用对象管理,这个更好
public:
static Information& getInstance(){
return info;
}
};
- 懒汉子模式
class Information{
private:
static Information* info = new Information();// 只能指针管理
public:
static Information getInstance(){
if(not info) info = new Information(); // 使用的时候才创建
return info;
}
};