C++ 设计模式——工厂模式
工厂模式是一种创建对象的设计模式,主要用于将对象的创建与使用分离。它提供了一种接口来创建对象,但由子类决定实例化哪个类。工厂模式有几种变体,包括简单工厂模式、工厂方法模式和抽象工厂模式。
这里写目录标题
- C++ 设计模式——工厂模式
- 1. 简单工厂模式
- 主要组成部分
- 代码实现
- 简单工厂模式模式的 UML 图
- 简单工厂模式 UML 图解析
- 优点和缺点
- 适用场景
- 2. 工厂方法模式
- 主要组成部分
- 代码实现
- 工厂方法模式模式的 UML 图
- 工厂方法模式 UML 图解析
- 优点和缺点
- 适用场景
- 3. 抽象工厂模式
- 主要组成部分
- 代码实现
- 抽象工厂模式模式的 UML 图
- 抽象工厂模式 UML 图解析
- 优点和缺点
- 适用场景
- 总结
C++ 设计模式——工厂模式
工厂模式是一种创建对象的设计模式,主要用于将对象的创建与使用分离。它提供了一种接口来创建对象,但由子类决定实例化哪个类。工厂模式有几种变体,包括简单工厂模式、工厂方法模式和抽象工厂模式。
1. 简单工厂模式
简单工厂模式是一种创建型设计模式,通过一个工厂类来负责对象的实例。这种模式将对象创建的细节封装在工厂类中,客户端无需知道具体的创建过程,只需通过工厂类获取对象实例即可。
引入**“简单工厂”设计模式的定义(实现意图):定义一个工厂类,该类的成员函数可以根据不同的参数创建并返回不同的类对象,被创建的对象所属的类一般都具有相同的父类。调用者无需关心创建对象的细节。
主要组成部分
- 工厂类(Factory):负责创建对象的类。它包含一个静态方法,根据传入的参数决定创建哪种具体产品。
- 产品接口(Product):定义了产品的基本行为和属性,所有具体产品类都需要实现这个接口。
- 具体产品类(ConcreteProduct):实现了产品接口的具体类,表示工厂可以创建的具体对象。
代码实现
以下代码,主要用简单工厂模式创建不同类型的怪物对象:
#include <iostream>
#include <string>
using namespace std;
//怪物父类
class Monster
{
public:
//构造函数
Monster(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Monster() {} //做父类时析构函数应该为虚函数
protected: //可能被子类访问的成员,用protected修饰
//怪物属性
int m_life; //生命值
int m_magic; //魔法值
int m_attack; //攻击力
};
//亡灵类怪物
class M_Undead :public Monster
{
public:
//构造函数
M_Undead(int life, int magic, int attack) :Monster(life, magic, attack)
{
cout << "一只亡灵类怪物来到了这个世界" << endl;
}
//其他代码略....
};
//元素类怪物
class M_Element :public Monster
{
public:
//构造函数
M_Element(int life, int magic, int attack) :Monster(life, magic, attack)
{
cout << "一只元素类怪物来到了这个世界" << endl;
}
//其他代码略....
};
//机械类怪物
class M_Mechanic :public Monster
{
public:
//构造函数
M_Mechanic(int life, int magic, int attack) :Monster(life, magic, attack)
{
cout << "一只机械类怪物来到了这个世界" << endl;
}
//其他代码略....
};
//--------------------------------
// 简单工厂模式
// 怪物工厂类
class MonsterFactory
{
public:
Monster* createMonster(string strmontype)
{
Monster* prtnobj = nullptr;
if (strmontype == "udd") //udd代表要创建亡灵类怪物
{
prtnobj = new M_Undead(300, 50, 80);
}
else if (strmontype == "elm") //ele代表要创建元素类怪物
{
prtnobj = new M_Element(200, 80, 100);
}
else if (strmontype == "mec") //mec代表要创建机械类怪物
{
prtnobj = new M_Mechanic(400, 0, 110);
}
return prtnobj;
}
};
// 使用示例
int main()
{
MonsterFactory facobj;
Monster* undead = facobj.createMonster("udd"); //产生了一只亡灵类怪物,当然这里必须知道"udd"代表的是创建亡灵类怪物
Monster* element = facobj.createMonster("elm"); //产生了一只元素类怪物
Monster* mechanic = facobj.createMonster("mec"); //产生了一只机械类怪物
// 释放内存
delete undead;
delete element;
delete mechanic;
return 0;
}
简单工厂模式模式的 UML 图
简单工厂模式 UML 图解析
-
类关系:
- 类之间的父子关系通过实线箭头表示。子类(如
M_Undead
、M_Element
、M_Mechanic
)与父类Monster
之间有一条实线箭头,指向父类。 MonsterFactory
类与具体怪物类(如M_Undead
、M_Element
、M_Mechanic
)之间存在虚线箭头,表示依赖关系。虚线箭头指向被实例化的类,表明MonsterFactory
负责创建这些怪物对象。
- 类之间的父子关系通过实线箭头表示。子类(如
-
稳定与变化:
- 创建怪物的代码只需与
MonsterFactory
类交互,因此调用createMonster
成员函数的代码是稳定的。 - 但若要增加新类型的怪物,就需要修改
MonsterFactory
类中的createMonster
函数,这部分代码是变化的。
- 创建怪物的代码只需与
-
信息隐藏:
- 如果
MonsterFactory
由第三方开发,开发者不希望暴露具体怪物类(如M_Undead
、M_Element
、M_Mechanic
)的细节。通过提供createMonster
接口,开发者可以创建不同类型的怪物,从而实现具体实现的隐藏,并确保创建怪物的代码与具体类解耦,避免模块间的相互影响。
- 如果
优点和缺点
优点:
-
简化客户端代码:客户端只需通过工厂类获取对象,而无需了解对象的具体创建过程。
-
集中管理对象创建:所有对象的创建逻辑集中在工厂类中,便于维护和管理。
-
易于扩展:新增产品时,只需在工厂类中添加相应的创建逻辑,客户端代码无需修改。
缺点:
-
违反开闭原则:增加新产品需要修改工厂类的代码,导致工厂类可能变得臃肿。
-
赖于字符串参数:客户端需要了解产品类型的字符串标识,可能导致错误和不易维护。
-
不适合产品种类过多的情况:如果产品种类增多,工厂类会变得复杂且难以管理。
适用场景
-
产品具有相似结构:当需要创建的对象具有相似的结构或特性,且种类较少时。
-
客户端不关心创建过程:客户端希望通过简单的接口获取对象实例,而不关心其创建细节。
-
快速原型开发:在项目初期,快速实现功能时,可以使用简单工厂模式减少复杂性。
-
动态创建对象:需要根据不同条件动态创建对象的情况,适合使用简单工厂模式。
2. 工厂方法模式
工厂方法模式是一种创建型设计模式,它通过定义一个接口用于创建对象,但由子类决定实例化哪个类。与简单工厂模式不同,工厂方法模式将对象的创建委托给子类,从而实现更好的扩展性和灵活性。
引入“工厂方法模式(实现意图):定义一个用于创建对象的接口,但由子类决定要实例化的类是哪一个。该模式使得某个类的实例化延迟到子类。
主要组成部分
- 产品接口(Product):定义了工厂方法所创建的对象的接口。
- 具体产品(ConcreteProduct):实现了产品接口的具体类。
- 工厂接口(Creator):声明了工厂方法,返回一个产品对象。
- 具体工厂(ConcreteCreator):实现了工厂接口,返回具体产品的实例。
代码实现
以下代码,主要用工厂方法模式创建不同类型的怪物对象:
#include <iostream>
#include <string>
using namespace std;
// 怪物父类
class Monster
{
public:
Monster(int life, int magic, int attack) : m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Monster() {} // 虚析构函数
protected:
int m_life; // 生命值
int m_magic; // 魔法值
int m_attack; // 攻击力
};
// 亡灵类怪物
class M_Undead : public Monster
{
public:
M_Undead(int life, int magic, int attack) : Monster(life, magic, attack)
{
cout << "一只亡灵类怪物来到了这个世界" << endl;
}
// 其他代码略....
};
// 元素类怪物
class M_Element : public Monster
{
public:
M_Element(int life, int magic, int attack) : Monster(life, magic, attack)
{
cout << "一只元素类怪物来到了这个世界" << endl;
}
// 其他代码略....
};
// 机械类怪物
class M_Mechanic : public Monster
{
public:
M_Mechanic(int life, int magic, int attack) : Monster(life, magic, attack)
{
cout << "一只机械类怪物来到了这个世界" << endl;
}
// 其他代码略....
};
// 工厂方法模式
// 所有工厂类的父类
class M_ParFactory
{
public:
virtual Monster* createMonster() = 0; // 纯虚函数
virtual ~M_ParFactory() {} // 虚析构函数
};
// 亡灵类怪物工厂
class M_UndeadFactory : public M_ParFactory
{
public:
virtual Monster* createMonster()
{
return new M_Undead(300, 50, 80); // 创建亡灵类怪物
}
};
// 元素类怪物工厂
class M_ElementFactory : public M_ParFactory
{
public:
virtual Monster* createMonster()
{
return new M_Element(200, 80, 100); // 创建元素类怪物
}
};
// 机械类怪物工厂
class M_MechanicFactory : public M_ParFactory
{
public:
virtual Monster* createMonster()
{
return new M_Mechanic(400, 0, 110); // 创建机械类怪物
}
};
// 全局创建怪物对象的函数
Monster* Gbl_CreateMonster(M_ParFactory* factory)
{
return factory->createMonster(); // 根据工厂创建怪物
}
// 创建怪物工厂子类模板
template <typename T>
class M_ChildFactory : public M_ParFactory
{
public:
virtual Monster* createMonster() {
return new T(300, 50, 80); // 创建具体类型的怪物
}
};
// 使用示例
int main()
{
// 使用具体工厂创建怪物
M_ParFactory* undeadFactory = new M_UndeadFactory();
Monster* undead = Gbl_CreateMonster(undeadFactory);
delete undead; // 释放内存
delete undeadFactory;
M_ParFactory* elementFactory = new M_ElementFactory();
Monster* element = Gbl_CreateMonster(elementFactory);
delete element; // 释放内存
delete elementFactory;
M_ParFactory* mechanicFactory = new M_MechanicFactory();
Monster* mechanic = Gbl_CreateMonster(mechanicFactory);
delete mechanic; // 释放内存
delete mechanicFactory;
// 使用模板工厂创建怪物
M_ChildFactory<M_Undead> undeadChildFactory;
Monster* undeadChild = Gbl_CreateMonster(&undeadChildFactory);
delete undeadChild; // 释放内存
return 0;
}
工厂方法模式模式的 UML 图
工厂方法模式 UML 图解析
- 类关系
Monster
:抽象基类,定义了怪物的基本属性(如m_life
、m_magic
、m_attack
)。M_Undead
、M_Element
、M_Mechanic
:具体类,继承自Monster
,实现不同类型的怪物。
- 工厂类
M_ParFactory
:抽象工厂类,定义了创建怪物的接口createMonster
。M_UndeadFactory
、M_ElementFactory
、M_MechanicFactory
:具体工厂类,继承自M_ParFactory
,分别负责创建不同类型的怪物。M_ChildFactory
:模板工厂类,能够创建任意类型的怪物,提供更灵活的创建方式。
- 稳定部分:
Gbl_CreateMonster
函数依赖的Monster
类和M_ParFactory
类属于稳定部分,不需要更改。
- 变化部分:
M_UndeadFactory
、M_ElementFactory
、M_MechanicFactory
类以及具体怪物类(如M_Undead
、M_Mechanic
)属于变化部分。Gbl_CreateMonster
函数不依赖于这些变化部分。
- 扩展新类型:
- 当需要引入新怪物类型(例如
M_Beast
)时,无需更改Gbl_CreateMonster
函数或修改MonsterFactory
的createMonster
方法。只需创建一个继承自Monster
的新类M_Beast
和一个继承自M_ParFactory
的新工厂类M_BeastFactory
,这符合开闭原则:对扩展开放,对修改关闭。
- 当需要引入新怪物类型(例如
- 隐藏实现细节:
- 如果
M_ParFactory
及其子类由第三方开发,工厂方法模式可以有效隐藏具体怪物类(如M_Undead
、M_Element
、M_Mechanic
),避免暴露给开发者。
- 如果
- 接口扩展:
M_ParFactory
的接口可以根据需要扩展,例如增加对 NPC(非玩家角色,如商人、路人等)的创建支持,或为createMonster
提供默认实现。
- 增加工厂类的代价:
- 增加新的工厂类是工厂方法模式的必然代价,但这为系统的灵活性和可扩展性提供了保障。
优点和缺点
- 优点:
-
符合开闭原则:新增产品时,只需创建新的产品类和相应的工厂类,无需修改现有代码。
-
减少耦合:客户端代码与具体产品类解耦,依赖于抽象工厂接口,增强了系统的灵活性。
-
扩展性强:可以方便地扩展新的产品类型,适合复杂系统的需求变化。
-
提高代码可读性:通过明确的工厂类和产品类的分离,代码结构更加清晰。
-
- 缺点:
-
类的数量增加:每新增一种产品都需要创建相应的工厂类,可能导致类的数量剧增,增加系统复杂性。
-
实现复杂性:对于简单产品的创建,工厂方法模式可能显得过于复杂,增加不必要的开销。
-
难以管理:如果产品种类过多,可能导致工厂类的管理变得困难。
-
适用场景
-
产品族:需要创建一系列相关或相互依赖的对象时,工厂方法模式非常适合。
-
产品类型变化频繁:当产品类型经常变化,且需要动态地扩展新产品时,工厂方法模式提供了良好的灵活性。
-
需要解耦的系统:当系统需要将对象创建与使用分离时,工厂方法模式可以有效降低耦合度。
-
复杂对象的创建:当对象的创建过程复杂,且可能涉及多个步骤时,工厂方法模式可以封装这些复杂性。
3. 抽象工厂模式
抽象工厂模式提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。它通常用于需要创建多个产品族的场景。
引入“抽象工厂模式”设计模式的定义(实现意图):提供一个接口,让该接口负责创建一系列相关或者相互依赖的对象,而无须指定它们具体的类。
主要组成部分
- 抽象工厂(AbstractFactory):定义创建抽象产品的接口。它声明了用于创建不同类型产品的方法。
- 具体工厂(ConcreteFactory):实现抽象工厂接口,创建具体产品。每个具体工厂负责生成一组相关的具体产品。
- 抽象产品(AbstractProduct):定义产品的接口。它为具体产品提供了一个公共的接口。
- 具体产品(ConcreteProduct):实现抽象产品接口的具体类。每个具体产品对应于一个具体工厂。
代码实现
以下是使用抽象工厂模式创建不同类型的怪物对象的代码示例:
#include <iostream>
#include <string>
using namespace std;
// 怪物父类
class Monster {
public:
Monster(int life, int magic, int attack) : m_life(life), m_magic(magic), m_attack(attack) {}
virtual ~Monster() {} // 虚析构函数
protected:
int m_life; // 生命值
int m_magic; // 魔法值
int m_attack; // 攻击力
};
// 沼泽亡灵类怪物
class M_Undead_Swamp : public Monster {
public:
M_Undead_Swamp(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个沼泽的亡灵类怪物来到了这个世界" << endl;
}
};
// 沼泽元素类怪物
class M_Element_Swamp : public Monster {
public:
M_Element_Swamp(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个沼泽的元素类怪物来到了这个世界" << endl;
}
};
// 沼泽机械类怪物
class M_Mechanic_Swamp : public Monster {
public:
M_Mechanic_Swamp(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个沼泽的机械类怪物来到了这个世界" << endl;
}
};
// 山脉亡灵类怪物
class M_Undead_Mountain : public Monster {
public:
M_Undead_Mountain(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个山脉的亡灵类怪物来到了这个世界" << endl;
}
};
// 山脉元素类怪物
class M_Element_Mountain : public Monster {
public:
M_Element_Mountain(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个山脉的元素类怪物来到了这个世界" << endl;
}
};
// 山脉机械类怪物
class M_Mechanic_Mountain : public Monster {
public:
M_Mechanic_Mountain(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个山脉的机械类怪物来到了这个世界" << endl;
}
};
// 城镇亡灵类怪物
class M_Undead_Town : public Monster {
public:
M_Undead_Town(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个城镇的亡灵类怪物来到了这个世界" << endl;
}
};
// 城镇元素类怪物
class M_Element_Town : public Monster {
public:
M_Element_Town(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个城镇的元素类怪物来到了这个世界" << endl;
}
};
// 城镇机械类怪物
class M_Mechanic_Town : public Monster {
public:
M_Mechanic_Town(int life, int magic, int attack) : Monster(life, magic, attack) {
cout << "一个城镇的机械类怪物来到了这个世界" << endl;
}
};
// 所有工厂类的父类
class M_ParFactory {
public:
virtual Monster* createMonster_Undead() = 0; // 创建亡灵类怪物
virtual Monster* createMonster_Element() = 0; // 创建元素类怪物
virtual Monster* createMonster_Mechanic() = 0; // 创建机械类怪物
virtual ~M_ParFactory() {} // 虚析构函数
};
// 沼泽地区的工厂
class M_Factory_Swamp : public M_ParFactory {
public:
virtual Monster* createMonster_Undead() override {
return new M_Undead_Swamp(300, 50, 120); // 创建沼泽亡灵类怪物
}
virtual Monster* createMonster_Element() override {
return new M_Element_Swamp(200, 80, 110); // 创建沼泽元素类怪物
}
virtual Monster* createMonster_Mechanic() override {
return new M_Mechanic_Swamp(400, 0, 90); // 创建沼泽机械类怪物
}
};
// 山脉地区的工厂
class M_Factory_Mountain : public M_ParFactory {
public:
virtual Monster* createMonster_Undead() override {
return new M_Undead_Mountain(300, 50, 80); // 创建山脉亡灵类怪物
}
virtual Monster* createMonster_Element() override {
return new M_Element_Mountain(200, 80, 100); // 创建山脉元素类怪物
}
virtual Monster* createMonster_Mechanic() override {
return new M_Mechanic_Mountain(600, 0, 110); // 创建山脉机械类怪物
}
};
// 城镇的工厂
class M_Factory_Town : public M_ParFactory {
public:
virtual Monster* createMonster_Undead() override {
return new M_Undead_Town(300, 50, 80); // 创建城镇亡灵类怪物
}
virtual Monster* createMonster_Element() override {
return new M_Element_Town(200, 80, 100); // 创建城镇元素类怪物
}
virtual Monster* createMonster_Mechanic() override {
return new M_Mechanic_Town(400, 0, 110); // 创建城镇机械类怪物
}
};
// 使用示例
int main() {
M_ParFactory* factory = new M_Factory_Swamp();
Monster* undead = factory->createMonster_Undead();
Monster* element = factory->createMonster_Element();
Monster* mechanic = factory->createMonster_Mechanic();
// 释放内存
delete undead;
delete element;
delete mechanic;
delete factory;
return 0;
}
抽象工厂模式模式的 UML 图
抽象工厂模式 UML 图解析
-
类与类之间的关系:
Monster
类是所有具体怪物类的父类,子类(如M_Undead_Swamp
、M_Element_Swamp
、M_Mechanic_Swamp
等)通过实线箭头与父类连接,箭头指向Monster
类,表示继承关系。M_ParFactory
是抽象工厂类,定义了创建不同类型怪物的接口。具体工厂类(如M_Factory_Swamp
、M_Factory_Mountain
、M_Factory_Town
)通过实线箭头与M_ParFactory
类连接,表示继承关系。
-
依赖关系:
- 具体工厂类(如
M_Factory_Swamp
等)与具体怪物类(如M_Undead_Swamp
等)之间存在虚线箭头,表示依赖关系。具体工厂类负责实例化具体怪物类的对象,箭头指向被实例化的类。
- 具体工厂类(如
-
稳定与变化部分:
- 稳定部分:
Monster
类和M_ParFactory
类是稳定部分,不需要频繁修改。 - 变化部分:具体怪物类(如
M_Undead_Swamp
、M_Element_Swamp
等)和具体工厂类(如M_Factory_Swamp
等)属于变化部分。当需要添加新类型的怪物时,只需增加新的具体工厂类和具体怪物类,而不需要修改稳定部分。
- 稳定部分:
-
扩展性:
- 当需要引入新类型的怪物(如
M_Beast
),只需创建一个新的具体工厂类(如M_Factory_Beast
)和对应的怪物类(如M_Beast
),而不需要更改现有的工厂接口。这符合开闭原则:对扩展开放,对修改关闭。
- 当需要引入新类型的怪物(如
-
隐藏实现细节:
- 如果
M_ParFactory
类及其具体实现由第三方开发,开发者只需通过抽象工厂接口与工厂交互,而无需了解具体的怪物类(如M_Undead_Swamp
等),实现了对具体实现的隐藏。
- 如果
-
接口扩展:
M_ParFactory
中的接口可以根据需要进行扩展,例如新增创建其他类型对象的方法(如 NPC),使得工厂能够支持更丰富的对象创建。
优点和缺点
优点:
-
解耦合:客户端代码与具体产品的实现解耦,便于维护和扩展。修改产品的实现不会影响到客户端。
-
一致性:可以确保一组产品的一致性,避免在创建产品时出现不匹配的情况。例如,创建一个特定类型的怪物时,工厂会确保所有相关的属性和行为都符合预期。
-
易于扩展:新增产品类型时,只需扩展工厂类和相应的产品类,而无需修改现有代码,符合开闭原则。
-
隔离变化:将产品的创建逻辑集中在工厂中,减少了产品类之间的依赖,便于管理和控制变化。
缺点:
-
系统复杂性增加:引入抽象工厂模式会增加系统的复杂性,特别是在产品种类较多时,工厂类和产品类的数量也会增加。
-
维护成本:随着产品族的增加,维护抽象工厂及其子类的成本可能会提升,特别是当需要对多个工厂进行修改时。
-
难以支持新产品:如果需要支持新类型的产品,可能需要修改现有的工厂接口和实现,这可能导致较大的代码变动。
-
运行时开销:由于使用了多层抽象,可能会引入一定的运行时开销,尤其是在频繁创建对象的场景中。
适用场景
-
产品族的创建:当系统需要创建一组相关或相互依赖的产品时,使用抽象工厂模式可以确保产品的一致性和完整性。
-
需要解耦的系统:当需要将产品的创建与使用分离,减少系统之间的耦合度时,抽象工厂模式是一个理想的选择。
-
需要支持多个产品变体:在需要支持不同变体的情况下,例如不同地区的怪物、不同类型的用户界面组件等,抽象工厂模式可以有效管理这些变体。
-
需要扩展产品类型:当系统需要频繁扩展新产品类型时,抽象工厂模式提供了良好的扩展机制,符合开闭原则。
-
框架设计:在设计框架或库时,抽象工厂模式可以为用户提供灵活的产品创建接口,用户可以根据需要实现具体的工厂类。
总结
-
代码实现复杂度:简单工厂模式最简单,工厂方法模式次之,抽象工厂模式最复杂。
-
工厂数量:简单工厂模式需要的工厂数量最少,工厂方法模式最多,抽象工厂模式有效减少工厂数量。
维护成本:随着产品族的增加,维护抽象工厂及其子类的成本可能会提升,特别是当需要对多个工厂进行修改时。 -
难以支持新产品:如果需要支持新类型的产品,可能需要修改现有的工厂接口和实现,这可能导致较大的代码变动。
-
运行时开销:由于使用了多层抽象,可能会引入一定的运行时开销,尤其是在频繁创建对象的场景中。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)