冷知识,当我们需要使用平底锅时,我们并不需要知道平底锅是怎么造的,坏了只需要再买就好了。至于造平底锅,全部交给我们的生产工厂就好。

蕴含这种创建对象思路的设计方法,我们称为"工厂模式"。
核心思想
工厂模式(Factory)是最常用的设计模式之一了,属于创建型,核心是将对象的创建过程与使用过程分离。它将对象的实例化封装在一个工厂类中,使客户端代码无需关心其构造细节。
工厂模式分为三类,简单工厂模式(Simple Factory),工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory),接下来将进行逐一讲解。

简单工厂模式
简单工厂模式特色就是简单粗暴,其可以根据传入参数的不同直接创建不同的对象。其又称为静态工厂模式,因为其通常用静态方法来创建对象,点开即用,避免重复。
在工厂这套"工厂体系"中,有三个角色需要我们实现:
**1、抽象产品角色:**是所有具体产品的基类或接口,统一接口规范,强制所有的基类实现相同的方法,确保客户端在使用时可以以统一的方式调用,隐藏实现细节。
**2、具体产品角色:**继承自抽象产品角色的具体实现类,提供特定类型对象的具体行为。这样一来就实现了多态,客户端代码只需要使用抽象产品而非具体产品,符合依赖倒置原则。
3、工厂角色:封装创建对象的逻辑,隔离客户端与具体类。
这里就以游戏开发中敌人的生成来举例吧。
cpp
#include <iostream>
using namespace std;
#include <string>
/*抽象产品角色*/
//定义接口
class Enemy {
public:
//统一行为:敌人攻击
virtual void Attack() = 0;
};
/*具体产品角色*/
class Goblin : public Enemy {
public:
//实现攻击
//(override显式声明重写基类虚函数)
void Attack() override {
cout << "你被哥布林用双手拥抱住了!" << endl;
}
};
class Slime : public Enemy {
public:
//实现攻击
void Attack() override {
cout << "你被史莱姆用身体包裹住了!" << endl;
}
};
/*工厂角色*/
class EnemyFactory {
public:
static Enemy* createEnemy(const string& name ) {
if (name == "Goblin") {
return new Goblin();
}
else if (name == "Slime") {
return new Slime();
}
else {
cout << "没有生成敌人" << endl;
return NULL;
}
}
};
int main() {
Enemy* A = EnemyFactory::createEnemy("Goblin");
Enemy* B = EnemyFactory::createEnemy("Slime");
A->Attack();
B->Attack();
return 0;
}
简单工厂的优点就是简单,我们使用对象时只需要知道对应的参数就可以了,大大减少了记忆负担。工厂类也为我们完成了任务分工,我们什么也不用管。但是这样一来,所有产品的创建逻辑都集成在工厂类中,扩展困难,不具备开闭原则的优势。
工厂方法模式
刚刚说到,简单工厂模式想要扩展创建新的产品时,需要修改工厂类的内部逻辑,这违背了开闭原则。那么当我们将工厂类中创建不同产品的逻辑分别封装到一个个子类工厂中,那我们创建新产品时就只需要创建新的子类工厂即可,从而实现高效扩展。
这时有四个角色需要我们实现:
**1、抽象产品角色:**具体产品的基类,统一接口规范。
**2、具体产品角色:**定义具体行为。
**3、抽象工厂角色:**定义子类工厂实现的规范,由其派生出子类工厂,不负责具体实现。
**4、具体工厂角色:**为抽象工厂的子类工厂,实现具体对象的创建逻辑,每个具体工厂对应一个具体产品。
同样,这里就把刚刚的简单工厂改造为工厂方法吧。
cpp
#include <iostream>
using namespace std;
/*抽象产品*/
//定义接口
class Enemy {
public:
virtual void Attack() = 0;
virtual ~Enemy(){}
};
/*具体产品*/
class Goblin : public Enemy {
public:
void Attack() override {
cout << "你被哥布林用双手拥抱住了!" << endl;
}
};
class Slime : public Enemy {
public:
void Attack() override {
cout << "你被史莱姆用身体包裹住了!" << endl;
}
};
/*抽象工厂*/
class EnemyFactory {
public:
//子类工厂接口
virtual Enemy* createEnemy() = 0;
virtual ~EnemyFactory(){}
};
/*具体工厂*/
//哥布林工厂
class GoblinFactory : public EnemyFactory {
Enemy* createEnemy() override {
return new Goblin();
}
};
//史莱姆工厂
class SlimeFactory : public EnemyFactory {
Enemy* createEnemy() override{
return new Slime();
}
};
int main() {
//需要创建具体工厂对象
EnemyFactory* goblin = new GoblinFactory();
EnemyFactory* slime = new SlimeFactory();
Enemy* A = goblin->createEnemy();
Enemy* B = slime->createEnemy();
A->Attack();
B->Attack();
delete goblin;
delete slime;
delete A;
delete B;
return 0;
}
现在的话代码就完全符合开闭原则了,系统的可扩展性增强了。因为其具体工厂都有唯一的抽象基类,所以工厂方法模式又叫多态方法模式,用户无需关注类的细节,甚至不需要知道具体产品的类名。但从上面代码也能看出,每次编写新的具体产品类时,还需要提供新的具体工厂类,有些增加开销。
抽象工厂模式
我们现在知道了,工厂模式是关注单个产品的,比如敌人工厂,专门生成各种敌人。但是我们的游戏系统中一定不光是有敌人存在的,还有友人,路人(中立)等同类型级别的生物存在。这时,当系统需要生产一组相关联的复杂对象而非单个简单对象,且这些对象属于不同的产品类别,抽象工厂模式是最佳选择。

在抽象工厂模式中,我们需要引入"产品等级结构"与"产品族"的概念。
**产品等级结构:**产品等级结构即产品的继承结构,就是抽象产品类和各个具体产品类构成一个产品等级结构。如上图中,近战生物产品作为抽象父类,近战敌人、近战友人、近战路人作为具体子类,他们就构成一个产品等级结构。
**产品族:**由同一个工厂生产的位于不同产品等级结构的一组产品。比如上图中的敌人工厂生产的近战敌人、远攻敌人,属于一个产品族。
但是如果将上图敌人生成工厂与我们以前使用的哥布林、史莱姆的例子直接结合的话,会形成嵌套抽象工厂。但我们要知道抽象工厂的核心目标是管理一个产品族,否则会违背单一职责原则,使结构臃肿,所以通常采用其他设计方法。我们也可以看出当产品等级结构只有一个时,抽象工厂模式就会退化为工厂方法模式。
那么理解之后,我们继续修改上文中所使用的代码实现抽象工厂模式。现在需要知道每一个产品等级结构不需要同等级,而需要具有强关联性,比如说,我们要生产哥布林,就不能只生产哥布林,还有其武器、防具等强相关的属性。
cpp
#include <iostream>
using namespace std;
/*产品等级结构1(抽象产品)------敌人*/
class Enemy {
public:
virtual void Attack() = 0;
virtual ~Enemy(){}
};
/*产品等级结构2(抽象产品)------武器*/
class Weapon {
public:
virtual void Hold() = 0;
virtual ~Weapon() {}
};
/*产品等级结构3(抽象产品)------防具*/
class Armor {
public:
virtual void Dress() = 0;
virtual ~Armor(){}
};
//划分完产品等级结构之后,开始定义具体产品,实现不同产品族
//产品族1:哥布林系列产品
/*具体产品(敌人)------哥布林特有(哥布林)*/
class Goblin : public Enemy {
public:
void Attack() override {
cout << "你被哥布林用双手拥抱住了!" << endl;
}
};
/*具体产品(武器)------哥布林特有(木棍)*/
class Stick : public Weapon {
public:
void Hold() override {
cout << "它在使用木棍对付你!" << endl;
}
};
/*具体产品(防具)------哥布林特有(肚兜)*/
class Bellyband : public Armor {
public:
void Dress() override {
cout << "别忘了它只穿了一个肚兜!" << endl;
}
};
//产品族2:史莱姆系列产品
/*具体产品(敌人)------史莱姆特有(史莱姆)*/
class Slime : public Enemy {
public:
void Attack() override {
cout << "你被史莱姆用身体包裹住了!" << endl;
}
};
/*具体产品(武器)------史莱姆特有(粘液)*/
class Mucus : public Weapon {
public:
void Hold() override {
cout << "它在使用粘液对付你!" << endl;
}
};
/*具体产品(防具)------史莱姆特有(没穿)*/
class NoCloth : public Armor {
public:
void Dress() override {
cout << "别忘了它什么都没有穿哦!" << endl;
}
};
/*抽象工厂*/
class EnemyFactory {
public:
//统一产品族的创建
virtual Enemy* createEnemy() = 0;
virtual Weapon* createWeapon() = 0;
virtual Armor* createArmor() = 0;
virtual ~EnemyFactory(){}
};
/*具体工厂------每个工厂对应一个产品族*/
//哥布林工厂
class GoblinFactory : public EnemyFactory {
Enemy* createEnemy() override {
return new Goblin();
}
Weapon* createWeapon() override {
return new Stick();
}
Armor* createArmor() override {
return new Bellyband();
}
};
//史莱姆工厂
class SlimeFactory : public EnemyFactory {
Enemy* createEnemy() override {
return new Slime();
}
Weapon* createWeapon() override {
return new Mucus();
}
Armor* createArmor() override {
return new NoCloth();
}
};
int main() {
//需要创建具体工厂对象
EnemyFactory* goblin = new GoblinFactory();
EnemyFactory* slime = new SlimeFactory();
Enemy* enemyA = goblin->createEnemy();
Enemy* enemyB = slime->createEnemy();
Weapon* weaponA = goblin->createWeapon();
Weapon* weaponB = slime->createWeapon();
Armor* armorA = goblin->createArmor();
Armor* armorB = slime->createArmor();
enemyA->Attack();
weaponA->Hold();
armorA->Dress();
enemyB->Attack();
weaponB->Hold();
armorB->Dress();
delete goblin;
delete slime;
delete enemyA, enemyB, weaponA, weaponB, armorA, armorB;
return 0;
}
这时,我们成功实现了抽象工厂模式,提供了一系列相关或相互依赖的对象的接口,隔离了具体类的生成。增加新的产品族和具体工厂时无需修改内部逻辑,符合开闭原则。缺点就是没办法直接添加新的产品对象,即新的产品等级结构。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。其常常用于以下场景:
1、需要创建一组相关或依赖的对象:这些产品通常都需要使用,比如敌人生成系统中,大家都有一套攻击方式、防御方式、移动方式等。
2、需要支持扩展产品族:比如创建新敌人"龙"的时候,正好也需要攻击方式、移动方式等一套模板式的行为。
3、系统不依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
4、属于同一个产品族的产品将在一起使用,协同工作。能够保证兼容性。
5、系统提供一个产品类的库(系统中一系列相关的产品类被组织为一个"库",被抽象工厂管理),所有的产品以同样的接口出现,从而使客户端不依赖于具体实现(只和抽象接口交互)。
小结
在游戏开发中,工厂模式的使用十分广泛。比如宝箱中生成随机属性的药水,可以根据工厂中不同产品或产品族可以切换的特点从而随机生成。还有职业选择,根据法师、战士等不同的特点配套不同的模组。总的来说,工厂模式的核心在于解耦,将创建与使用分离,尤其适合统一管理一整套相关对象。

如有补充纠正欢迎各位留言。