C++设计模式创建型模式———简单工厂模式、工厂方法模式、抽象工厂模式

文章目录

  • 一、引言
  • 二、简单工厂模式
  • 三、工厂方法模式
  • 三、抽象工厂模式
  • 四、总结

一、引言

创建一个类对象的传统方式是使用关键字new , 因为用 new 创建的类对象是一个堆对象,可以实现多态。工厂模式通过把创建对象的代码包装起来,实现创建对象的代码与具体的业务逻辑代码相隔离的目的(将对象的创建和使用进行解耦)。

试想,如果创建一个类A的对象,可能会写出A* pa=new A()这样的代码行,但当给类A的构造函数增加一个参数时,所有利用new创建类A对象的代码行全部需要修改,如果通过工厂模式把创建类A对象的代码统一放到某个位置,则对于诸如给类A的构造函数增加参数之类的问题,只需要修改一个位置就可以了。

工厂模式属于创建型模式,一般可以细分为3种:简单工厂模式、工厂方法模式和抽象

工厂模式主要包括简单工厂模式工厂方法模式抽象工厂模式


二、简单工厂模式

简单工厂模式并不属于设计模式中的经典模式之一,但它是工厂模式的基础。它通过一个工厂类根据传入的参数来决定创建哪种类的对象。

简单工厂模式的核心思想是:将对象的创建集中管理,通过一个工厂类来实例化对象,而不是让客户端直接使用 new 操作符来创建对象。这样就可以将对象的创建和使用分离,提高代码的可维护性和灵活性。

简单工厂模式主要由以下三部分组成:

  1. 工厂类(Factory):负责对象的创建,根据传入的参数,决定创建哪种产品对象。
  2. 抽象产品类(Abstract Product):定义了产品的接口或抽象类,规定了所有产品必须实现的功能。
  3. 具体产品类(Concrete Product):实现了抽象产品类,代表了具体要创建的产品。

这里以单机闯关打斗类游戏游戏开发来阐述。

游戏中的主角需要通过攻击并杀死怪物来进行闯关,策划规定,在该游戏中,暂时有3类怪物(后面可能会增加新的怪物种类),分别是亡灵类怪物、元素类怪物、机械类怪物,每种怪物都有一些各自的特点(细节略),当然,这些怪物还有一些共同特点,例如同主角一样,都有生命值、魔法值、攻击力3个属性,为此,创建一个Monster(怪物)类作为父类,而创建M_Undead(亡灵类怪)、MElement(元素类怪)和M_Mechanic(机械类怪)作为子类是合适的。针对怪物,程序定义了如下几个类:

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_Mechanic :public Monster
{
public:M_Mechanic(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;}//...
}; 

当需要在游戏的战斗场景中产生怪物时,传统方法可以使用new直接产生各种怪物,
例如在main主函数中可以加人如下代码:

Monster*pM1=new M_Undead(300,50,80;//产生了一只亡灵类怪物
Monster*pM2=new M_Element(200,80,100;//产生了一只元素类怪物
Monster*pM3=new M_Mechanic(400,0,110;//产生了一只机械类怪物
//释放资源
delete pM1;
delete pM2;
delete pM3;

上面这种创建怪物的写法虽然合法,但不难看到,当创建不同种类的怪物时,避免不了直接与多个怪物类(M_UndeadM_ElementM_Mechanic)打交道,这属于一种依赖具体类的紧耦合,因为需要知道这些类的名字,尤其是随着游戏内容的不断增加,怪物的种类也可能会不断增加。

如果通过某个扮演工厂角色的类(怪物工厂类)来创建怪物,则意味着创建怪物时不再使用new关键字,而是通过该工厂类来进行,这样的话,即便将来怪物的种类增加,main主函数中创建怪物的代码也可以尽量保持稳定。通过工厂类,避免了在mai~n函数中(也可以在任何其他函数中)直接使用new创建对象时必须知道具体类名(这是一种依赖具体类的紧耦合关系)的情形发生,实现了创建怪物的代码与各个具体怪物类对象要实现的业务逻辑代码隔离,这就是简单工厂模式的实现思路。

当然,和使用new创建对象的直观性比,显然简单工厂模式的实现思路是绕了弯的。下面就创建一个怪物工厂类MonsterFactory,用这个工厂类来生产(产生)出各种不同种类的怪物,代码如下:

// 简单工厂类
class MonsterFactory {
public:static unique_ptr<Monster> createMonster(const string& type, int life, int magic, int attack) {if (type == "undead") {return make_unique<M_Undead>(life, magic, attack);}else if (type == "mechanic") {return make_unique<M_Mechanic>(life, magic, attack);}else if (type == "element") {return make_unique<M_Element>(life, magic, attack);}else {return nullptr; // 无效类型}}
};

通过上面的代码可以看到,createMonster成员函数的形参是一个字符串,代表怪物类型。虽然通过工厂创建怪物不再需要直接与各个怪物类打交道,但必须通过一个标识告诉怪物工厂类要创建哪种怪物,这就是该字符串的作用。当然,不使用字符串而使用一个整型数字也没问题,只要能标识出不同的怪物类型即可。createMonster成员函数返回的是Monster*的智能指针,这个所有怪物类的父类指针以支持多态。

也可以把createMonster函数实现为类的成员方法。

auto undead = MonsterFactory::createMonster("undead", 100, 50, 10);
auto mechanic = MonsterFactory::createMonster("mechanic", 120, 40, 15);
auto element = MonsterFactory::createMonster("element", 80, 60, 20);

代码经过改造后,创建各种怪物时就不必面对M_UndeadM_ElementM_Mechanic等具体的怪物类,只要面对MonsterFactory类即可。当然,其实main主函数创建对象时遇到的麻烦(依赖具体怪物类)依旧存在,只是被转嫁给了MonsterFactory类而已。其实,依赖这件事本身并不会因为引人设计模式而完全消失,程序员能做的是把这种依赖的范围尽量缩小(例如缩小到MonsterFactory类的createMonster成员函数中),从而避免依赖关系遍布整个代码(所有需要创建怪物对象的地方),这就是所谓的封装变化(把容易变化的代码段限制在一个小范围内),就可以在很大程度上提高代码的可维护性和可扩展性,否则可能会导致一修改代码就要修改一大片的困境。例如以往如果这样写代码:

Monster*pM1=new M_Undead(300,50,80;

那么一旦要对圆括号中的参数类型进行修改或者新增参数,则所有涉及new M_Undead的代码段可能都要修改,但采用简单工厂模式后,只需要修改MonsterFactory类的createMonster成员函数,确实省了很多事。
MonsterFactory类的实现也有缺点。最明显的缺点就是当引人新的怪物类型时,需要修改createMonster成员函数的源码来增加新的if判断分支,从而支持对新类型怪物的创建工作,这违反了面向对象程序设计的一个原则一一开闭原则。

在这里插入图片描述

与类之间以实线箭头表示父子关系,子类(M_Undead、M_Element、M_Mechanic)
与父类(Monster)之间有一条带箭头的实线,箭头的方向指向父类。

MonsterFactory类与M_Undead、M_Element、M_Mechanic类之间的虚线箭头表示箭头连接的两个类之间存在着依赖关系(一个类引l用另一个类),换句话说,虚线箭头表示一个类(MonsterFactory)实例化另外一个类(M_Undead、M_Element、M_Mechanic)的对象,箭头指向被实例化对象的类。

由于创建怪物只需要MonsterFactory类的createMonster函数,因此创建怪物的代码是稳定的,但是如果新增怪物就需要在工厂类修改函数代码,因此createMonster函数是变化的。

简单工厂模式的优缺点:

引入简单工厂设计模式:定义一个工厂类,该类的一个函数可以根据不同的参数创建并返回不同的类对象,被创建的对象所属的类一般具有相同的父类。使用者不必关心创建对象的细节。

违反开闭原则:每当新增一个产品类型时,都需要修改工厂类的代码,这违背了“对扩展开放,对修改封闭”的设计原则。

不适合产品种类过多的情况:如果产品种类过多,工厂类的逻辑会变得很复杂,难以维护。


三、工厂方法模式

工厂模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。工厂方法模式解决了简单工厂模式中扩展性差的问题。它将对象的创建过程延迟到子类中,由不同的子类决定实例化哪个类。每一种具体产品都有对应的工厂。

在上面的简单工厂的代码中,我们引入新的怪物就需要就该工厂的成员函数(即新增if判断分支),这样导致代码过于琐碎,且难以维护。

工厂方法模式采用新增新的工厂类的方法支持新怪物类型(不影响已有代码),满足了开闭原则。这种方式的灵活性更强,实现也更为复杂,同时也要引入更多的新类(主要是工厂类)。

我们仍使用简单工厂的例子。

在工厂方法模式中,不是用一个工厂类MonsterFactory来解决创建多种类型怪物的问
题,而是用多个工厂类来解决创建多种类型怪物的问题。而且,针对每种类型的怪物,都需
要创建一个对应的工厂类,例如,当前要创建3种类型的怪物M_UndeadM_Element
M_Mechanic,那么,就需要创建3个工厂类,例如分别命名为M_UndeadFactory
M_ElementFactoryM_MechanicFactory。而且这3个工厂类还会共同继承自同一个工厂父类,例如将该工厂父类命名为M_ParFactory(工厂抽象类)。

如果将来策划要求引人第四种类型的怪物,那么毫无疑问,需要为该种类型的怪物增加
对应的一个新工厂类,当然该新工厂类依然继承自M_ParFactory类。

从上面的描述,可以初步看出,工厂方法模式通过增加新的工厂类来符合开闭原则(对扩展开放,对修改关闭),但付出的代价是需要增加多个新的工厂类。

下面开始改造简单工厂模式中实现的代码。实现所有工厂类的父类M_ParFactory(等价于将简单工厂模式中的工厂类MonsterFactory进行抽象),代码如下:

// 抽象工厂类 所有工厂类的父类
class M_ParFactory {
public:virtual unique_ptr<Monster> createMonster() = 0; // 工厂方法virtual ~M_ParFactory(){}
};

然后,针对每个具体的怪物子类,都需要创建一个相关的工厂类,所以,针对
M_UndeadM_ElementM_Mechanic类,创建3个工厂类M_UndeadFactoryM_ElementFactoryM_MechanicFactory,代码如下:

// 亡灵怪物工厂
class M_UndeadFactory : public M_ParFactory {
public:unique_ptr<Monster> createMonster() override {return make_unique<M_Undead>(300,50,80);}
};// 机械怪物工厂
class M_MechanicFactory : public M_ParFactory {
public:unique_ptr<Monster> createMonster() override {return make_unique<M_Mechanic>(400,0,110);}
};// 元素怪物工厂
class M_ElementFactory : public M_ParFactory {
public:unique_ptr<Monster> createMonster() override {return make_unique<M_Element>(200,80,100);}
};

有了这3个怪物工厂类之后,可以创建一个全局函数Gbl_CreateMonster来处理怪物对象的生成,代码如下:

unique_ptr<Monster> Gbl_CreateMonster(M_ParFactory* factory )
{return factory->createMonster();//createMonster虚函数扮演了多态new的行为,factory//指向的具体怪物工厂类不同,创建的怪物对象也不同
}

从现在的代码可以看到,Gbl_CreateMonster作为创建怪物对象的核心函数,并不依赖
于具体的M_UndeadM_ElementM_Mechanic怪物类,只依赖于Monster类(Gbl_CreateMonster的返回类型)和M_ParFactory类(Gbl_CreateMonster的形参类型),变化的部分被隔离到调用Gbl_CreateMonster函数的地方去了。

在main主函数中,通过如下代码来通过各自的工厂生产各自的产品:

M_ParFactory* p_ud_fy = new M_UndeadFactory();//多态工厂,注意指针类型
auto  pM1 = Gbl_CreateMonster(p_ud_fy);//产生了一只亡灵类怪物,也是多态,注意返
//回类型,当然也可以直接写成Monster
//pM1 =p_ud_fy->createMonster();
M_ParFactory* p_elm_fy = new M_ElementFactory();
auto pM2 = Gbl_CreateMonster(p_elm_fy);//产生了一只元素类怪物
M_ParFactory* p_mec_fy = new M_MechanicFactory();
auto pM3 = Gbl_CreateMonster(p_mec_fy);//产生了一只机械类怪物
delete p_ud_fy;

从上述代码可以看到,创建怪物对象时,不需要记住具体怪物类的名称,但需要知道创
建该类怪物的工厂的名称。

引人工厂方法设计模式的定义(实现意图):定义一个用于创建对象的接口(M_ParFactory类中的createMonster成员函数,这其实就是工厂方法,工厂方法模式的名字也是由此而来),但由子类(M_UndeadFactoryM_ElementFactoryM_MechanicFactory)决定要实例化的类是哪一个。该模式使得某个类(M_UndeadM_ElementM_Mechanic)的实例化延迟到子类(M_UndeadFactoryM_ElementFactoryM_MechanicFactory)。

在这里插入图片描述

  • Gbl_CreateMonster函数所依赖的Monster类和M_ParFactory类都属于稳定部分(不需要改动的类)。
  • M_UndeadFactoryM_ElementFactoryM_MechanicFactory类以及M_UndeadM_ElementM_Mechanic类都属于变化部分。Gbl_CreateMonster函数并不依赖
    于这些变化部分。

当出现一个新的怪物类型时,既不需要更改GbI_CreateMonster函数,也不需要像简单工厂模式那样修改MonsterFactory类中的createMonster成员函数来增加新的if分支,除了要添加继承自Monster的类之外,只需要为新的怪物类型增加一个新的继承自主工厂的工厂类即可。这正好符合面向对象程序设计的开闭原则一对扩展开放,对修改关闭(封闭)。所以,一般可以认为,将简单工厂模式的代码通过把工厂类进行抽象改造成符合开闭原则后的代码,就变成了工厂方法模式的代码。

当然我们也可以使用模板来实现,(前提是用户知道各种怪兽名)

template<class T>
class M_ChildFactory :public M_ParFactory {
public:virtual unique_ptr<Monster> createMonster() override {return make_unique<T>(200, 80, 100);}
};//使用
M_ChildFactory<M_Undead> myf;
unique_ptr<Monster> pm = myf.createMonster();

工厂方法的模式结构

在这里插入图片描述

  1. 产品 (Product) 将会对接口进行声明。 对于所有由创建者及其子类构建的对象, 这些接口都是通用的。

  2. 具体产品 (Concrete Products) 是产品接口的不同实现。

  3. 创建者 (Creator) 类声明返回产品对象的工厂方法。 该方法的返回对象类型必须与产品接口相匹配。

    可以将工厂方法声明为抽象方法, 强制要求每个子类以不同方式实现该方法。 或者, 你也可以在基础工厂方法中返回默认产品类型。

    注意, 尽管它的名字是创建者, 但它最主要的职责并不是创建产品。 一般来说, 创建者类包含一些与产品相关的核心业务逻辑。 工厂方法将这些逻辑处理从具体产品类中分离出来。 打个比方, 大型软件开发公司拥有程序员培训部门。 但是, 这些公司的主要工作还是编写代码, 而非生产程序员。

  4. 具体创建者 (Concrete Creators) 将会重写基础工厂方法, 使其返回不同类型的产品。

注意, 并不一定每次调用工厂方法都会创建新的实例。 工厂方法也可以返回缓存、 对象池或其他来源的已有对象。

另外,必须注意,工厂方法模式往往需要创建一个与产品等级结构(层次)相同的工厂等
级结构,这也增加了新类的层次结构和数目。

工厂方法模式的优缺点:

  • 优点:符合开闭原则。添加新的产品类时,只需要添加对应的具体工厂类和产品类,不需要修改已有的工厂类代码。更容易增加新的产品类型。
  • 缺点:增加了类的数量,每增加一种产品类型都需要创建相应的工厂类。而且客户需要知道每个产品的具体工厂类。

三、抽象工厂模式

抽象工厂模式适用于需要创建一系列相关或依赖的对象的场景。它不仅定义了工厂方法,还定义了多个产品对象的创建方法。它能创建一系列相关的对象, 而无需指定其具体类。

继续使用之前的例子,前面开发的单机闯关打斗类游戏,随着游戏内容越来越丰富,游戏中战斗场景(关卡)数量和类型不断增加,从原来的在城镇中战斗逐步进入在沼泽地战斗、在山脉地区战斗等。于是,策划把怪物种类进一步按照场景进行了分类,怪物目前仍旧保持3类:亡灵类、元素类和机械类。战斗场景也分为3类:沼泽地区、山脉地区和城镇。这样来划分的话,整个游戏中目前就有9类怪物:沼泽地区的亡灵类、元素类、机械类怪物;山脉地区的亡灵类、元素类、机械类怪物;城镇中的亡灵类、元素类、机械类怪物。策划规定每个区域的同类型怪物能力上差别很大,例如,沼泽地中的亡灵类怪物攻击力比城镇中的亡灵类怪物高很多,山脉地区的机械类怪物会比沼泽地区的机械类怪物生命值高许多。

这样看起来,从怪物父类Monster继承而来的怪物子类就会由原来的3种M_Undead
M_ElementM_Mechanic变为9种,按照这样的怪物分类方式,使用工厂方法模式创建怪
物对象则需要创建多达9个工厂子类,但如果一个工厂子类能够生产不止一种具有相同规
则的怪物对象,那么就可以有效地减少所创建的工厂子类数量,这就是抽象工厂(Abstract Factory)模式的核心思想。

有两个概念在抽象工厂模式中经常被提及,分别是“产品等级结构”和“产品族”。绘制一个坐标轴,把前述的9种怪物放人其中:

在这里插入图片描述

在上图中,所需的三个工厂分别是Y轴的三个。抽象工厂模式是按照产品族来生产产品,一个地方一个工厂,这个工厂就负责生产本产地的所有产品。

那么我们保留之前的Monster父类,删除原来的三个怪物子类,重新引入9个怪物。

// 沼泽亡灵类怪物
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;}
};

因为工厂是针对一个产品族进行生产的,因此需要创建1个工厂父类,3个工厂子类。

// 工厂父类
class M_ParFactory {
public:virtual unique_ptr<Monster> createUndead() = 0;   // 创建亡灵类怪物virtual unique_ptr<Monster> createElement() = 0;   // 创建元素类怪物virtual unique_ptr<Monster> createMechanic() = 0;  // 创建机械类怪物virtual ~M_ParFactory() {} // 虚析构函数
};// 沼泽工厂
class M_Factory_Swamp : public M_ParFactory {
public:unique_ptr<Monster> createUndead() override {return make_unique<M_Undead_Swamp>(300,50,120); // 创建沼泽亡灵类怪物}unique_ptr<Monster> createElement() override {return make_unique<M_Element_Swamp>(200,80,110); // 创建沼泽元素类怪物}unique_ptr<Monster> createMechanic() override {return make_unique<M_Mechanic_Swamp>(400,0,90); // 创建沼泽机械类怪物}
};// 山脉工厂
class M_Factory_Mountain : public M_ParFactory {
public:unique_ptr<Monster> createUndead() override {return make_unique<M_Undead_Mountain>(300,50,120); // 创建山脉亡灵类怪物}unique_ptr<Monster> createElement() override {return make_unique<M_Element_Mountain>(200,80,110); // 创建山脉元素类怪物}unique_ptr<Monster> createMechanic() override {return make_unique<M_Mechanic_Mountain>(400,0,90); // 创建山脉机械类怪物}
};// 城镇工厂
class M_Factory_Town : public M_ParFactory {
public:unique_ptr<Monster> createUndead() override {return make_unique<M_Undead_Town>(300,50,120); // 创建城镇亡灵类怪物}unique_ptr<Monster> createElement() override {return make_unique<M_Element_Town>(200,80,110); // 创建城镇元素类怪物}unique_ptr<Monster> createMechanic() override {return make_unique<M_Mechanic_Town>(400,0,90); // 创建城镇机械类怪物}
};//使用工厂创建怪物
int main() {// 创建不同的工厂实例unique_ptr<M_ParFactory> swampFactory = make_unique<M_Factory_Swamp>();unique_ptr<M_ParFactory> mountainFactory = make_unique<M_Factory_Mountain>();unique_ptr<M_ParFactory> townFactory = make_unique<M_Factory_Town>();// 使用工厂创建怪物实例auto swampUndead = swampFactory->createUndead();auto mountainElement = mountainFactory->createElement();auto townMechanic = townFactory->createMechanic();return 0; // 返回0表示程序成功结束
}
  • 如果果游戏中的战斗场景新增加一个森林类型的场景而怪物种类不变(依旧是亡灵类怪物、元素类怪物和机械类怪物),则只需要增加一个新的子工厂类,并继承自M_ParFactory,而后在新的子工程类中实现createMonster_UndeadcreateMonster_ElementcreateMonster_Mechanic虚函数(接口)即可。这种代码实现方式符合开闭原则,也就是通过增加新代码而不是修改原有代码来为游戏增加新功能(对森林类型场景中怪物的创建支持)。
  • 如果游戏中新增加了一个新的怪物种类,则此时不但要新增3个继承自Monster的子类来分别支持沼泽龙类怪物、山脉龙类怪物、城镇龙类怪物,还必须修改工厂父类M_ParFactory来增加新的虚函数以支持创建龙类怪物,各个工厂子类也需要增加对新怪物的支持。这种在工厂类中通过修改已有代码来扩充游戏功能的方式显然不符合开闭原则。所以此种情况下不适合使用抽象工厂模式。

在这里插入图片描述

抽象工厂的模式结构

在这里插入图片描述

下面再分析一下工厂方法模式与抽象工厂模式的区别

工厂方法模式适用于一个工厂生产一个产品的需求,抽象工厂模式适用于一个工厂生产多个产品(一个产品族)的需求笔)。另外,无论是产品族数量较多还是产品等级结构数量较多,抽象工厂的优势都将更加明显。

引人抽象工厂设计模式的定义(实现意图):提供一个接口(AbstractFactory),让该接口负责创建一系列相关或者相互依赖的对象,而无须指定它们具体的类。

  • 优点:适用于创建一组相关产品的场景,可以确保产品间的一致性。
  • 缺点:增加了系统的复杂性,每新增一个产品族都需要修改工厂类。

四、总结

简单工厂、工厂方法和抽象工厂是三种常见的创建型设计模式,它们各自有不同的特点和应用场景。下面做个总结:

  • 代码实现复杂度上,简单工厂模式最简单,工厂方法模式次之,抽象工厂模式最复杂。简单工厂模式中的代码修改得符合开闭原则,就变成了工厂方法模式,修改工厂方法模式的代码使一个工厂支持对多个具体产品的生产,就变成了抽象工厂模式。

  • 从需要的工厂数量上,简单工厂模式需要的工厂数量最少,工厂方法模式需要的工厂数量最多,抽象工厂模式能够有效地减少工厂方法模式所需要的工厂数量(可以将工厂方法模式看作抽象工厂模式的一种特例一一抽象工厂模式中的工厂若只创建一种对象就是工厂方法模式)。

  • 从实际应用上,当项目中的产品数量比较少时考虑使用简单工厂模式,如果项目稍大一点或者为了满足开闭原则,则可以使用工厂方法模式,而对于大型项目中有众多厂商并且每个厂商都生产一系列产品时应考虑使用抽象工厂模式。

  • 而且简单工厂模式不能遵守开放-封闭原则,工厂和抽象工厂模式可以。

  • 工厂模式创建的产品对象相对简单,抽象工厂模式创建的产品对象相对复杂

    • 工厂模式创建的对象对应的类不需要提供抽象类【这产品类组件中没有可变因素】
    • 抽象工厂模式创建的对象对应的类有抽象的基类【这个产品类组件中有可变因素】

在许多设计工作的初期都会使用工厂方法模式(较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、 原型模式或生成器模式(更灵活但更加复杂)。抽象工厂模式通常基于一组工厂方法, 但也可以使用原型模式来生成这些类的方法。可以同时使用工厂方法和迭代器模式来让子类集合返回不同类型的迭代器, 并使得迭代器与集合相匹配。原型并不基于继承, 因此没有继承的缺点。 另一方面, 原型需要对被复制对象进行复杂的初始化。 工厂方法基于继承, 但是它不需要初始化步骤。工厂方法是模板方法模式的一种特殊形式。 同时, 工厂方法可以作为一个大型模板方法中的一个步骤。

而且可以将抽象工厂和桥接模式搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/58768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python爬虫抓取豆瓣数据教程

环境准备 在开始之前&#xff0c;你需要确保你的Python环境已经安装了以下库&#xff1a; requests&#xff1a;用于发送HTTP请求。BeautifulSoup&#xff1a;用于解析HTML文档。 如果你还没有安装这些库&#xff0c;可以通过以下命令安装&#xff1a; pip install requests…

代码-画图函数示例

热力图 import matplotlib.pyplot as plt import seaborn as sns import numpy as npdef create_heatmap(people, categories, dataNone, title热力图, xlabel类别, ylabel人员,value_range(0.6, 0.95), figsize(10, 6),cmapYlOrRd, decimal_places3):"""创建热…

2024最新Twitter养号全面指南,品牌起号必看!

X (Twitter)作为活跃用户数以亿计的社交媒体平台&#xff0c;用户数依然在不断增长&#xff0c;其中巨大的流量吸引着个人用户与品牌和卖家。 Twitter养号是有必要的&#xff0c;有大量案例表明养好号&#xff0c;可以大幅度降低账号被冻结的几率&#xff0c;并提升账号的稳定…

百度如何打造AI原生研发新范式?

&#x1f449;点击即可下载《百度AI原生研发新范式实践》资料 2024年10月23-25日&#xff0c;2024 NJSD技术盛典暨第十届NJSD软件开发者大会、第八届IAS互联网架构大会在南京召开。本届大会邀请了工业界和学术界的专家&#xff0c;优秀的工程师和产品经理&#xff0c;以及其它行…

基于大语言模型(LLM)自主Agent 智能体综述

近年来,LLM(Large Language Model)取得了显著成功,并显示出了达到人类智能的巨大潜力。基于这种能力,使用LLM作为中央控制器来构建自助Agent,以获得类人决策能力。 Autonomous agents 又被称为智能体、Agent。指能够通过感知周围环境、进行规划以及执行动作来完成既定任务。…

电脑怎么设置开机密码:保障个人信息安全的第一步

在数字化时代&#xff0c;个人信息的安全至关重要。电脑作为我们日常工作和生活中不可或缺的设备&#xff0c;存储了大量的私人数据和敏感信息。为了防止未经授权的访问&#xff0c;设置开机密码是保护个人隐私和信息安全的基本措施之一。本文将详细介绍如何在不同操作系统下为…

分析 std::optional 的使用与常见错误

文章目录 引言常见错误及解决方案1. 错误使用 std::optional 变量进行算术运算2. 错误检查 std::optional 是否有值3. 忽视 std::optional 的默认值 结论 引言 std::optional 是 C17 引入的一个模板类&#xff0c;用于表示可能有也可能没有值的情况。它特别适用于函数返回值&a…

DB-GPT系列(二):DB-GPT部署(镜像一键部署、源码部署)

一、简介 DB-GPT 是一个开源项目&#xff0c;其将大语言模型 LLM 与数据库紧密结合。该项目主要致力于探索如何让预训练的大规模语言模型&#xff08;例如 GPT&#xff09;能够直接与数据库进行交互&#xff0c;从而生成更为准确且信息丰富的回答。 DB-GPT部署后能否直接使用…

Web组件之 Listener (监听器)

文章目录 1.1 Listener概述1.2 Listener快速入门① xml版本② 注解版本 1.3 案例&#xff1a;模拟spring框架 1.1 Listener概述 ​ JavaWeb 中的监听器是监听 ServletContext HttpSession HttpServletRequest 三个数据域对象创建和销毁以及监听数据域对象中数据的变化&#xf…

【论文翻译】IJCAI 2019 | Graph WaveNet:用于深度时空图建模的Graph WaveNet

论文题目Graph WaveNet for Deep Spatial-Temporal Graph Modeling作者团队Zonghan Wu, Shirui Pan, Guodong Long, Jing Jiang, Chengqi Zhang机构澳大利亚悉尼科技大学人工智能中心 (UTS) 和 澳大利亚莫纳什大学发表会议IJCAI 2019论文链接https://www.ijcai.org/proceedings…

Java数组的定义与使用

今天来学习Java数组的定义与使用 目录 1 数组的基本概念1.1 数组的意义1.2 数组的定义1.3 数组的创建及初始化1.3.1 数组的创建1.3.2 数组的初始化 1.4 数组的使用1.4.1 数组中的元素访问1.4.2 遍历数组运行结果运行结果 2 数组是引用类型2.1 初始 JVM 的内存分布2.2 基本类型变…

https://tieba.baidu.com/p/9247698007

微深节能的库区智能化无人天车管理系统结合了格雷母线技术&#xff0c;提供了一种高精度的定位解决方案。格雷母线系统能够实现连续或断续的位置检测&#xff0c;精度高达≤5mm&#xff0c;适用于需要高精度作业的场景&#xff0c;如货物搬运和堆放。这种系统通过实时交互&…

创作里程碑:纪念日回顾与展望

目录 机缘&#xff1a;创作者初心 1. 实战项目 2. 日常学习 3. 技术交流 4. 总结 收获&#xff1a;创作者动力 创作与工作、学习的关系 憧憬&#xff1a;职业规划与创作规划 职业规划&#xff1a; 创作规划&#xff1a; 机缘&#xff1a;创作者初心 回望自己踏上…

软考(中级-软件设计师)数据库篇(1101)

第6章 数据库系统基础知识 一、基本概念 1、数据库 数据库&#xff08;Database &#xff0c;DB&#xff09;是指长期存储在计算机内的、有组织的、可共享的数据集合。数据库中的数据按一定的数据模型组织、描述和存储&#xff0c;具有较小的冗余度、较高的数据独立性和扩展…

go-zero 的使用

目录 1. 生成 user api 服务 2. 生成 user rpc 服务 3. 生成 user model 模型 4. 编写 user rpc 服务 1 修改配置文件 user.yaml 2 添加 user model 依赖 3 添加用户登录逻辑 Login 5. 编写 user api 服务 1 修改配置文件user.yaml 2 添加 user rpc 依赖 3 添加用户…

基金委:目前资助率过低,危害大,应提升至30~35%,增大青年、面上项目经费

国家基金委 近日&#xff0c;国家基金委发表题为《近十年 NSF资助率和资助强度上升 对我国科学基金资助工作的启示》文章&#xff0c;文章基于近十年 NSF总体资助变化,提出对我国科学基金资助工作启示&#xff0c;阐述了国家自然基金项目资助率下降情况、危害&#xff0c;并提…

NPM 包开发与优化全面指南

前言 Hey, 我是 Immerse系列文章首发于【Immerse】&#xff0c;更多内容请关注该网站转载说明&#xff1a;转载请注明原文出处及版权声明&#xff01; 1. 理解 NPM 包的结构 1.1 package.json 文件&#xff1a;包的核心 package.json文件是 NPM 包的中央配置&#xff0c;定…

学Linux的第六天

目录 账户和组管理 工作组管理 创建工作组groupadd 修改工作组groupmod 添加/删除组成员gpasswd 删除工作组groupdel 查看用户登录系统的情况 users查看当前登录系统的用户 last命令 lastlog命令 w命令 显示登录到系统的用户信息 who命令 Linux文件系统权限 文件…

来康生命科技有限公司心率监测解决方案在健身房与康养机构的应用探索

引言 随着科技的日新月异&#xff0c;智能健康服务正逐步成为现代健康管理不可或缺的一环。来康生命科技有限公司&#xff0c;凭借其在智能物联集成交互领域的自主创新能力&#xff0c;推出了一款集蓝牙物联网、蓝牙手环、数据云与管理终端于一体的心率监测解决方案。此方案专…

在 .NET 8 Web API 中实现 Entity Framework 的 Code First 方法

本次介绍分为3篇文章&#xff1a; 1&#xff1a;.Net 8 Web API CRUD 操作.Net 8 Web API CRUD 操作-CSDN博客 2&#xff1a;在 .Net 8 API 中实现 Entity Framework 的 Code First 方法https://blog.csdn.net/hefeng_aspnet/article/details/143229912 3&#xff1a;.NET …