这里写目录标题

  • 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 图

简单工厂模式 UML 图解析

  • 类关系

    • 类之间的父子关系通过实线箭头表示。子类(如 M_UndeadM_ElementM_Mechanic)与父类 Monster 之间有一条实线箭头,指向父类。
    • MonsterFactory 类与具体怪物类(如 M_UndeadM_ElementM_Mechanic)之间存在虚线箭头,表示依赖关系。虚线箭头指向被实例化的类,表明 MonsterFactory 负责创建这些怪物对象。
  • 稳定与变化

    • 创建怪物的代码只需与 MonsterFactory 类交互,因此调用 createMonster 成员函数的代码是稳定的。
    • 但若要增加新类型的怪物,就需要修改 MonsterFactory 类中的 createMonster 函数,这部分代码是变化的。
  • 信息隐藏

    • 如果 MonsterFactory 由第三方开发,开发者不希望暴露具体怪物类(如 M_UndeadM_ElementM_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 图

工厂方法模式 UML 图解析

  • 类关系
    • Monster:抽象基类,定义了怪物的基本属性(如 m_lifem_magicm_attack)。
    • M_UndeadM_ElementM_Mechanic:具体类,继承自 Monster,实现不同类型的怪物。
  • 工厂类
    • M_ParFactory:抽象工厂类,定义了创建怪物的接口 createMonster
    • M_UndeadFactoryM_ElementFactoryM_MechanicFactory:具体工厂类,继承自 M_ParFactory,分别负责创建不同类型的怪物。
    • M_ChildFactory:模板工厂类,能够创建任意类型的怪物,提供更灵活的创建方式。
  • 稳定部分
    • Gbl_CreateMonster 函数依赖的 Monster 类和 M_ParFactory 类属于稳定部分,不需要更改。
  • 变化部分
    • M_UndeadFactoryM_ElementFactoryM_MechanicFactory 类以及具体怪物类(如 M_UndeadM_Mechanic)属于变化部分。Gbl_CreateMonster 函数不依赖于这些变化部分。
  • 扩展新类型
    • 当需要引入新怪物类型(例如 M_Beast)时,无需更改 Gbl_CreateMonster 函数或修改 MonsterFactorycreateMonster 方法。只需创建一个继承自 Monster 的新类 M_Beast 和一个继承自 M_ParFactory 的新工厂类 M_BeastFactory,这符合开闭原则:对扩展开放,对修改关闭。
  • 隐藏实现细节
    • 如果 M_ParFactory 及其子类由第三方开发,工厂方法模式可以有效隐藏具体怪物类(如 M_UndeadM_ElementM_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 图

抽象工厂模式 UML 图解析

  • 类与类之间的关系

    • Monster 类是所有具体怪物类的父类,子类(如 M_Undead_SwampM_Element_SwampM_Mechanic_Swamp 等)通过实线箭头与父类连接,箭头指向 Monster 类,表示继承关系。
    • M_ParFactory 是抽象工厂类,定义了创建不同类型怪物的接口。具体工厂类(如 M_Factory_SwampM_Factory_MountainM_Factory_Town)通过实线箭头与 M_ParFactory 类连接,表示继承关系。
  • 依赖关系

    • 具体工厂类(如 M_Factory_Swamp 等)与具体怪物类(如 M_Undead_Swamp 等)之间存在虚线箭头,表示依赖关系。具体工厂类负责实例化具体怪物类的对象,箭头指向被实例化的类。
  • 稳定与变化部分

    • 稳定部分Monster 类和 M_ParFactory 类是稳定部分,不需要频繁修改。
    • 变化部分:具体怪物类(如 M_Undead_SwampM_Element_Swamp 等)和具体工厂类(如 M_Factory_Swamp 等)属于变化部分。当需要添加新类型的怪物时,只需增加新的具体工厂类和具体怪物类,而不需要修改稳定部分。
  • 扩展性

    • 当需要引入新类型的怪物(如 M_Beast),只需创建一个新的具体工厂类(如 M_Factory_Beast)和对应的怪物类(如 M_Beast),而不需要更改现有的工厂接口。这符合开闭原则:对扩展开放,对修改关闭。
  • 隐藏实现细节

    • 如果 M_ParFactory 类及其具体实现由第三方开发,开发者只需通过抽象工厂接口与工厂交互,而无需了解具体的怪物类(如 M_Undead_Swamp 等),实现了对具体实现的隐藏。
  • 接口扩展

    • M_ParFactory 中的接口可以根据需要进行扩展,例如新增创建其他类型对象的方法(如 NPC),使得工厂能够支持更丰富的对象创建。

优点和缺点

优点

  • 解耦合:客户端代码与具体产品的实现解耦,便于维护和扩展。修改产品的实现不会影响到客户端。

  • 一致性:可以确保一组产品的一致性,避免在创建产品时出现不匹配的情况。例如,创建一个特定类型的怪物时,工厂会确保所有相关的属性和行为都符合预期。

  • 易于扩展:新增产品类型时,只需扩展工厂类和相应的产品类,而无需修改现有代码,符合开闭原则。

  • 隔离变化:将产品的创建逻辑集中在工厂中,减少了产品类之间的依赖,便于管理和控制变化。

缺点

  • 系统复杂性增加:引入抽象工厂模式会增加系统的复杂性,特别是在产品种类较多时,工厂类和产品类的数量也会增加。

  • 维护成本:随着产品族的增加,维护抽象工厂及其子类的成本可能会提升,特别是当需要对多个工厂进行修改时。

  • 难以支持新产品:如果需要支持新类型的产品,可能需要修改现有的工厂接口和实现,这可能导致较大的代码变动。

  • 运行时开销:由于使用了多层抽象,可能会引入一定的运行时开销,尤其是在频繁创建对象的场景中。

适用场景

  • 产品族的创建:当系统需要创建一组相关或相互依赖的产品时,使用抽象工厂模式可以确保产品的一致性和完整性。

  • 需要解耦的系统:当需要将产品的创建与使用分离,减少系统之间的耦合度时,抽象工厂模式是一个理想的选择。

  • 需要支持多个产品变体:在需要支持不同变体的情况下,例如不同地区的怪物、不同类型的用户界面组件等,抽象工厂模式可以有效管理这些变体。

  • 需要扩展产品类型:当系统需要频繁扩展新产品类型时,抽象工厂模式提供了良好的扩展机制,符合开闭原则。

  • 框架设计:在设计框架或库时,抽象工厂模式可以为用户提供灵活的产品创建接口,用户可以根据需要实现具体的工厂类。

总结

  • 代码实现复杂度:简单工厂模式最简单,工厂方法模式次之,抽象工厂模式最复杂。

  • 工厂数量:简单工厂模式需要的工厂数量最少,工厂方法模式最多,抽象工厂模式有效减少工厂数量。
    维护成本:随着产品族的增加,维护抽象工厂及其子类的成本可能会提升,特别是当需要对多个工厂进行修改时。

  • 难以支持新产品:如果需要支持新类型的产品,可能需要修改现有的工厂接口和实现,这可能导致较大的代码变动。

  • 运行时开销:由于使用了多层抽象,可能会引入一定的运行时开销,尤其是在频繁创建对象的场景中。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐