介绍

设计模式分为创建型模式、结构型模式和行为模式。创建型模式主要是在创建对象的时候使用的,接下来以植物大战僵尸作为例子。

工厂模式

简单工厂模式

僵尸类是所有僵尸的基类,僵尸类派生了普通僵尸类、路障僵尸类。

僵尸工厂由一个静态函数或者全局函数组成,通过对函数传参不同来创建不同种类的僵尸。这里通过传入字符串

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();

抽象工厂模式

抽象工厂其实是工厂方法模式的一个扩展,目前只有一种具体的实体,即僵尸。但是在真正一把游戏中,其实还有植物,阳光,子弹等等,他们也都是类。

假设额外实现一个植物类,再派生单发射手和双发射手。再实现一个游戏工厂,有两个成员变量(一个是抽象植物工厂类,一个是抽象僵尸工厂类)

接下来实现僵尸类及其工厂(工厂方法模式),植物类及其工厂(工厂方法模式)以及一个游戏类和游戏工厂。

  1. 僵尸类及僵尸工厂(参考工厂方法模式)
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);
    }
}
  1. 植物类及植物工厂(同僵尸类及僵尸工厂)
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);
    }
}
  1. 抽象游戏类及具体游戏,抽象游戏中包括了僵尸工厂和植物工厂。然后创建了一个游戏工厂,游戏工厂用来创建游戏,其实也是用来创建一系列工厂的。然后实现了简单游戏工厂和困难游戏工厂。
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) :抽象工程的一个具体实现,比如路障僵尸工厂类。
  • 聚合产品:里面有若干个抽象工厂指针,用于实现多态
  • 聚合抽象工厂:用于生产聚合产品的抽象工厂
  • 聚合具体工厂:用于生产聚合产品的具体工厂,通过改变聚合产品中的指针来实现。

然后介绍三种模式:

  • 简单工厂模式:实现抽象产品和具体产品,然后实现一个工厂类(含有静态函数)或者直接实现一个函数,该函数左右时返回抽象产品指针,该函数可以通过传入不同参数来实现创建不同的具体产品。
  • 工厂方法模式:实现抽象产品和具体产品,实现抽象工厂和具体工厂。抽象工厂定义了一些规定(比如创建产品),具体工厂实现这些规定时可以采用不同的方法,以达到实现创建不同产品的目的。
  • 抽象工厂模式:生产工厂的工厂,实现抽象产品和具体产品以及对应的抽象工厂和具体工厂,实现聚合产品以及对应的抽象工厂和具体工厂。

生成器/创建者模式

定义一个实体创建者基类,它定义了一些接口,比如设置血量,加载音乐,加载图片,设定动画间隔等等,但是一定要包括一个返回实体函数,这个返回实体函数用于返回真正的实体。对于植物和僵尸其实这些都是通用的,但是僵尸和植物也会有一些不同点。因此,再由实体创建者基类派生出僵尸创建者类和植物创建者类。

  1. 创造者基类与其派生类(植物实体创建者,僵尸实体创建者),这里两个实体只是举了个例子而已。
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;
    }
}
  1. 创建主管,主管中可以创建植物实体或者僵尸实体,并且能够返回实体。当然,这个主管类可以多保存一会,而不是直接把实体返回,这里采用简单的方法而已
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;
    }
};

单例模式的核心在于,整个工程只会有一个实例,并且这个实例可以被其他地方获取,而且所有通过这个类获取的实例都是同一个,但是需要手动保证线程安全。

实例分为饿汉子和懒汉子模式,饿汉子就是一开始就创建实例,这样会导致工程启动很慢。懒汉子模式就是,在使用的时候创建,但是会在第一次使用的时候卡一下。

  1. 饿汉子模式
class Information{
private:
    // static Information* info = new Information();// 指针管理
    static Information info; // 直接使用对象管理,这个更好
public:
    static Information& getInstance(){
        return info;
    }
};
  1. 懒汉子模式
class Information{
private:
    static Information* info = new Information();// 只能指针管理
public:
    static Information getInstance(){
        if(not info) info = new Information();  // 使用的时候才创建
        return info;
    }
};