一、引言
创建一个类对象的传统方式是使用关键字new
, 因为用 new
创建的类对象是一个堆对象,可以实现多态。工厂模式通过把创建对象的代码包装起来,实现创建对象的代码与具体的业务逻辑代码相隔离的目的(将对象的创建和使用进行解耦)。
试想,如果创建一个类A的对象,可能会写出A* pa=new A()
这样的代码行,但当给类A的构造函数增加一个参数时,所有利用new
创建类A对象的代码行全部需要修改,如果通过工厂模式把创建类A对象的代码统一放到某个位置,则对于诸如给类A的构造函数增加参数之类的问题,只需要修改一个位置就可以了。
工厂模式属于创建型模式,一般可以细分为3种:简单工厂模式、工厂方法模式和抽象
工厂模式主要包括简单工厂模式、工厂方法模式和抽象工厂模式。
二、简单工厂模式
简单工厂模式并不属于设计模式中的经典模式之一,但它是工厂模式的基础。它通过一个工厂类根据传入的参数来决定创建哪种类的对象。
简单工厂模式的核心思想是:将对象的创建集中管理,通过一个工厂类来实例化对象,而不是让客户端直接使用 new
操作符来创建对象。这样就可以将对象的创建和使用分离,提高代码的可维护性和灵活性。
简单工厂模式主要由以下三部分组成:
- 工厂类(Factory):负责对象的创建,根据传入的参数,决定创建哪种产品对象。
- 抽象产品类(Abstract Product):定义了产品的接口或抽象类,规定了所有产品必须实现的功能。
- 具体产品类(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_Undead
、M_Element
、M_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_Undead
、M_Element
、M_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_Undead
、M_Element
、
M_Mechanic
,那么,就需要创建3个工厂类,例如分别命名为M_UndeadFactory
、
M_ElementFactory
、M_MechanicFactory
。而且这3个工厂类还会共同继承自同一个工厂父类,例如将该工厂父类命名为M_ParFactory
(工厂抽象类)。
如果将来策划要求引人第四种类型的怪物,那么毫无疑问,需要为该种类型的怪物增加
对应的一个新工厂类,当然该新工厂类依然继承自M_ParFactory
类。
从上面的描述,可以初步看出,工厂方法模式通过增加新的工厂类来符合开闭原则(对扩展开放,对修改关闭),但付出的代价是需要增加多个新的工厂类。
下面开始改造简单工厂模式中实现的代码。实现所有工厂类的父类M_ParFactory
(等价于将简单工厂模式中的工厂类MonsterFactory
进行抽象),代码如下:
// 抽象工厂类 所有工厂类的父类
class M_ParFactory {
public:
virtual unique_ptr<Monster> createMonster() = 0; // 工厂方法
virtual ~M_ParFactory(){}
};
然后,针对每个具体的怪物子类,都需要创建一个相关的工厂类,所以,针对
M_Undead
、M_Element
、M_Mechanic
类,创建3个工厂类M_UndeadFactory
、M_ElementFactory
和M_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_Undead
、M_Element
、M_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_UndeadFactory
、M_ElementFactory
、M_MechanicFactory
)决定要实例化的类是哪一个。该模式使得某个类(M_Undead
、M_Element
、M_Mechanic
)的实例化延迟到子类(M_UndeadFactory
、M_ElementFactory
、M_MechanicFactory
)。
Gbl_CreateMonster
函数所依赖的Monster
类和M_ParFactory类都属于稳定部分(不需要改动的类)。M_UndeadFactory
、M_ElementFactory
、M_MechanicFactory
类以及M_Undead
、M_Element
、M_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();
工厂方法的模式结构
-
产品 (Product) 将会对接口进行声明。 对于所有由创建者及其子类构建的对象, 这些接口都是通用的。
-
具体产品 (Concrete Products) 是产品接口的不同实现。
-
创建者 (Creator) 类声明返回产品对象的工厂方法。 该方法的返回对象类型必须与产品接口相匹配。
可以将工厂方法声明为抽象方法, 强制要求每个子类以不同方式实现该方法。 或者, 你也可以在基础工厂方法中返回默认产品类型。
注意, 尽管它的名字是创建者, 但它最主要的职责并不是创建产品。 一般来说, 创建者类包含一些与产品相关的核心业务逻辑。 工厂方法将这些逻辑处理从具体产品类中分离出来。 打个比方, 大型软件开发公司拥有程序员培训部门。 但是, 这些公司的主要工作还是编写代码, 而非生产程序员。
-
具体创建者 (Concrete Creators) 将会重写基础工厂方法, 使其返回不同类型的产品。
注意, 并不一定每次调用工厂方法都会创建新的实例。 工厂方法也可以返回缓存、 对象池或其他来源的已有对象。
另外,必须注意,工厂方法模式往往需要创建一个与产品等级结构(层次)相同的工厂等
级结构,这也增加了新类的层次结构和数目。
工厂方法模式的优缺点:
- 优点:符合开闭原则。添加新的产品类时,只需要添加对应的具体工厂类和产品类,不需要修改已有的工厂类代码。更容易增加新的产品类型。
- 缺点:增加了类的数量,每增加一种产品类型都需要创建相应的工厂类。而且客户需要知道每个产品的具体工厂类。
三、抽象工厂模式
抽象工厂模式适用于需要创建一系列相关或依赖的对象的场景。它不仅定义了工厂方法,还定义了多个产品对象的创建方法。它能创建一系列相关的对象, 而无需指定其具体类。
继续使用之前的例子,前面开发的单机闯关打斗类游戏,随着游戏内容越来越丰富,游戏中战斗场景(关卡)数量和类型不断增加,从原来的在城镇中战斗逐步进入在沼泽地战斗、在山脉地区战斗等。于是,策划把怪物种类进一步按照场景进行了分类,怪物目前仍旧保持3类:亡灵类、元素类和机械类。战斗场景也分为3类:沼泽地区、山脉地区和城镇。这样来划分的话,整个游戏中目前就有9类怪物:沼泽地区的亡灵类、元素类、机械类怪物;山脉地区的亡灵类、元素类、机械类怪物;城镇中的亡灵类、元素类、机械类怪物。策划规定每个区域的同类型怪物能力上差别很大,例如,沼泽地中的亡灵类怪物攻击力比城镇中的亡灵类怪物高很多,山脉地区的机械类怪物会比沼泽地区的机械类怪物生命值高许多。
这样看起来,从怪物父类Monster
继承而来的怪物子类就会由原来的3种M_Undead
、
M_Element
、M_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_Undead
、createMonster_Element
、createMonster_Mechanic
虚函数(接口)即可。这种代码实现方式符合开闭原则,也就是通过增加新代码而不是修改原有代码来为游戏增加新功能(对森林类型场景中怪物的创建支持)。 - 如果游戏中新增加了一个新的怪物种类,则此时不但要新增3个继承自
Monster
的子类来分别支持沼泽龙类怪物、山脉龙类怪物、城镇龙类怪物,还必须修改工厂父类M_ParFactory
来增加新的虚函数以支持创建龙类怪物,各个工厂子类也需要增加对新怪物的支持。这种在工厂类中通过修改已有代码来扩充游戏功能的方式显然不符合开闭原则。所以此种情况下不适合使用抽象工厂模式。
抽象工厂的模式结构
下面再分析一下工厂方法模式与抽象工厂模式的区别:
工厂方法模式适用于一个工厂生产一个产品的需求,抽象工厂模式适用于一个工厂生产多个产品(一个产品族)的需求笔)。另外,无论是产品族数量较多还是产品等级结构数量较多,抽象工厂的优势都将更加明显。
引人抽象工厂设计模式的定义(实现意图):提供一个接口(AbstractFactory
),让该接口负责创建一系列相关或者相互依赖的对象,而无须指定它们具体的类。
- 优点:适用于创建一组相关产品的场景,可以确保产品间的一致性。
- 缺点:增加了系统的复杂性,每新增一个产品族都需要修改工厂类。
四、总结
简单工厂、工厂方法和抽象工厂是三种常见的创建型设计模式,它们各自有不同的特点和应用场景。下面做个总结:
-
代码实现复杂度上,简单工厂模式最简单,工厂方法模式次之,抽象工厂模式最复杂。简单工厂模式中的代码修改得符合开闭原则,就变成了工厂方法模式,修改工厂方法模式的代码使一个工厂支持对多个具体产品的生产,就变成了抽象工厂模式。
-
从需要的工厂数量上,简单工厂模式需要的工厂数量最少,工厂方法模式需要的工厂数量最多,抽象工厂模式能够有效地减少工厂方法模式所需要的工厂数量(可以将工厂方法模式看作抽象工厂模式的一种特例一一抽象工厂模式中的工厂若只创建一种对象就是工厂方法模式)。
-
从实际应用上,当项目中的产品数量比较少时考虑使用简单工厂模式,如果项目稍大一点或者为了满足开闭原则,则可以使用工厂方法模式,而对于大型项目中有众多厂商并且每个厂商都生产一系列产品时应考虑使用抽象工厂模式。
-
而且简单工厂模式不能遵守开放-封闭原则,工厂和抽象工厂模式可以。
-
工厂模式创建的产品对象相对简单,抽象工厂模式创建的产品对象相对复杂
- 工厂模式创建的对象对应的类不需要提供抽象类【这产品类组件中没有可变因素】
- 抽象工厂模式创建的对象对应的类有抽象的基类【这个产品类组件中有可变因素】
在许多设计工作的初期都会使用工厂方法模式(较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、 原型模式或生成器模式(更灵活但更加复杂)。抽象工厂模式通常基于一组工厂方法, 但也可以使用原型模式来生成这些类的方法。可以同时使用工厂方法和迭代器模式来让子类集合返回不同类型的迭代器, 并使得迭代器与集合相匹配。原型并不基于继承, 因此没有继承的缺点。 另一方面, 原型需要对被复制对象进行复杂的初始化。 工厂方法基于继承, 但是它不需要初始化步骤。工厂方法是模板方法模式的一种特殊形式。 同时, 工厂方法可以作为一个大型模板方法中的一个步骤。
而且可以将抽象工厂和桥接模式搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。