> 文章列表 > 什么是工厂模式?

什么是工厂模式?

什么是工厂模式?

文章目录

  • 00 | 基础知识
  • 01 | 简单工厂模式
    • 框架
    • 实现
    • 应用场景
    • 小结
  • 02 | 工厂方法模式
    • 框架
    • 实现
    • 应用场景
    • 小结
  • 03 | 抽象工厂模式
    • 框架
    • 实现
    • 应用场景
    • 小结
  • 04 | 总结

前面学习了设计模式的基本概念(设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结),知道了三大类的设计模式(其中最出名的当属 Gang of Four (GoF) 的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式),其中创建型设计模式较为简单,结构型设计模式以及行为型设计模式较之复杂一点,这里主要学习以下创建型设计模式当中的《工厂模式》
创建型模式专注于创建对象,如在使用某个功能的时候,只需要创建这个类的实例对象即可,而不用去关心这个功能的实现过程,这样将对象的创建和对象的调用划分开来,即创建的归创建,调用的归调用。
工厂模式在我的理解中就是把创建的动作整合在一起,通过“工厂”来“掌管”所有的创建动作,而根据这个“掌管”程序可以将之分为简单工厂模式、工厂方法模式、抽象工厂模式

00 | 基础知识

在这里插入图片描述

工厂模式中常用到“抽象类”,那么先来了解一下什么是抽象类,而要说“抽象类”,又不得不先提一下“纯虚函数”

  • 纯虚函数

    纯虚函数是一个在基类中声明的虚函数,并且在基类中仅声明这个虚函数,而不进行实际的实现,实际的实现需要派生类进行实现(一旦类中有纯虚函数,则表明这个类是一个抽象类)C++代码中格式如下

      virtual 函数类型 func() = 0;
    
  • 抽象类

    抽象类仅描述了类的行为和功能,而无须完成类的特定实现

    设计抽象类的目的是为了给其他类提供一个可以继承的基类,抽象类本身不能被用于实例化,而只能作为接口进行使用。

    如果在代码中对一个抽象类进行了实例化,那么会导致编译出错,这是因为抽象类中包含了没有定义的纯虚函数,在C++中,我们把只能用于被继承而不能直接创建对象的类称之为抽象类,这种基类不能直接生成对象,而只有被继承后,并重写其虚函数后,才能使用。

01 | 简单工厂模式

在这里插入图片描述

简单工厂模式,顾名思义就是一个简单的工厂模式……
类比一下现实中的小作坊,对外面的人来说,小作坊就是生产产品的地方,只要告诉小作坊,我们需要什么,他就会生产相应的产品;对里面的人来说,小作坊就是干活的地方,接到客户的要求后,可能整个产品的所有东西都在一个小房间里完成

框架

想象一下上面的小作坊中,生产一个手机壳的流程是什么?

  1. 客户给小作坊提出制作一个XX型号手机壳;

  2. 小作坊根据XX手机型号,通知XX手机型号生产线进行产品生产;

  3. 对应的产品线打模出实际的手机壳;

  4. XX手机壳制作完成,交付给客户

根据上面的流程,可知框架实现需要一个工厂类代表小作坊,一个产品类代表某个型号的手机生产线,其中工厂类只负责接收订单后下发通知,而不做实际生产动作(就像办公室里的领导),所以简单工厂模式的框架如下

  1. 再定义一个产品抽象类,这个类提供一些通用的接口声明,负责所有产品线的生产动作准则

  2. 从产品抽象类中派生出对应产品的实际产品类,负责实际的生产动作

  3. 首先定义一个工厂类,这个类提供一个外部接口,负责接收外部参数后创建对应的产品实例

实现

以上面的手机壳小作坊为例,通过C++进行简单实现

  • 产品抽象类

    class AbstractPhoneCase
    {public:AbstractPhoneCase(){};virtual ~AbstractPhoneCase(){};virtual void MakePhoneCase() = 0;
    };
    
  • 具体产品类

    class Xiaomi : public AbstractPhoneCase
    {public:Xiaomi(){}~Xiaomi(){};void MakePhoneCase(){cout << "Xiaomi phone case is finished!" << endl;}
    };class Apple : public AbstractPhoneCase
    {public:Apple(){}~Apple(){};void MakePhoneCase(){cout << "Apple phone case is finished!" << endl;}
    };class Error : public AbstractPhoneCase
    {public:Error(){};~Error(){};void MakePhoneCase(){cout << "The phone model could not be found" << endl;}
    };
    
  • 工厂类

    class PhoneFactory
    {public:PhoneFactory(){cout << "Welcome to Phone Factory" << endl;};~PhoneFactory(){};AbstractPhoneCase* getPhoneModelName(string i_strModelName){if (0 == i_strModelName.length() || ("Xiaomi" != i_strModelName && "Apple" != i_strModelName)){return new Error;}if ("Xiaomi" == i_strModelName){return new Xiaomi;}else{return new Apple;}}
    };
    
  • 测试

    int main()
    {PhoneFactory* phonefac = new PhoneFactory();string ModelName = "";cout << "Please select Phone Model: ";cin >> ModelName;phonefac->getPhoneModelName(ModelName)->MakePhoneCase();delete phonefac;return 0;
    }
    

简单工厂模式测试结果

应用场景

简单工厂模式主要运用在功能需求明确,后期基本不会进行新增功能的简单软件系统上,因为它所能创建的产品类都是必须事先就考虑清楚。

小结

简单来说,简单工厂模式通常就是创建一个工厂类XXX,里面有一个对外静态接口,根据接收外部参数,返回不同的从派生类的实例对象,每一个派生类都只对应一个产品线。

需要扩展的时候,需要衍生新的派生类,以及在工厂类中增加新的派生类实例创建语句,这一系列操作在日常代码中很常见,但是如果这时候要生产其他不是手机壳的产品的话,就会破坏了面向对象设计的单一责任原则,也违反了高内聚责任分配原则,这时候就可以引申出工厂方法模式

02 | 工厂方法模式

在这里插入图片描述

工厂方法模式,顾名思义就是工厂 + 方法的模式……
类比一下电子圣城 —— 深圳华强北,在这里能买到各种各样“正版”的电子产品,只有你想不到,没有你买不到,扯远了,回归正题。
说白了,工厂模式就是把同一类别的产品独立为一个工厂进行生产,与简单工厂模式的区别就是“从一个工厂扩大到了N个工厂”,公司做大做强了

框架

假如要去华强北进货,想买一批“正版Airpos”,一批“正版Ipad pencil”,那么流程应该像下面这样

  1. 找到某个可信度较高的档口,询问两件产品价格

  2. 档口接到这笔订单后,通知背后的耳机工厂和电容笔工厂来活了

  3. 两个工厂接到活后,根据对应的型号进行生产

  4. 产品生产完毕,档口将货物给出

跟小作坊买手机壳的流程中,对于客户而言没有变化,但是对于商家工厂而言,从一个工厂生产变成了两个工厂生产,所以工厂方法模式就是工厂 + 方法的模式,每一个工厂提供一个类别的方法,其框架如下

  1. 定义一个抽象工厂:用于提供实际工厂类的通用方法准则;

  2. 定义多个具体工厂:用于通知对应类别产品线;

  3. 定义一个抽象产品:同于提供实际产品线所通用的方法准则;

  4. 定义多个具体产品:用于进行实际产品的生产;

实现

以去华强被进货为例,以C++代码实现如下:

  • 抽象产品类

    class AbstractProduct
    {public:AbstractProduct(){};virtual ~AbstractProduct(){};virtual void makeProduct() = 0;
    };
    
  • 抽象工厂类

    class AbstractFactory
    {public:AbstractFactory(){};virtual ~AbstractFactory(){};virtual AbstractProduct* NotifyProductLine() = 0;
    };
    
  • 具体产品类

    class Airpos : public AbstractProduct
    {public:Airpos(){};~Airpos(){};void makeProduct(){cout << "Airpos is finished!" << endl;}
    };class Pencil : public AbstractProduct
    {public:Pencil(){};~Pencil(){};void makeProduct(){cout << "Pencil is finished!" << endl;}
    };class ProError : public AbstractProduct
    {public:ProError(){};~ProError(){};void makeProduct(){cout << "The product in this factory could not be found" << endl;}
    };
    
  • 具体工厂类

    class EarPhoneFactory : public AbstractFactory
    {public:EarPhoneFactory(){};~EarPhoneFactory(){};AbstractProduct* NotifyProductLine(){cout << "Notify Earphone product line" << endl;return new Airpos;}
    };class CondenserPenFactory : public AbstractFactory
    {public:CondenserPenFactory(){};~CondenserPenFactory(){};AbstractProduct* NotifyProductLine(){cout << "Notify Condenser pen product line" << endl;return new Pencil;}
    };
    
  • 测试

    int main()
    {EarPhoneFactory* epFac = new EarPhoneFactory();CondenserPenFactory* cpFac = new CondenserPenFactory();epFac->NotifyProductLine()->makeProduct();cpFac->NotifyProductLine()->makeProduct();delete epFac, cpFac;return 0;
    }
    

工厂方法测试结果

应用场景

由于工厂方法模式的特性,客户端不必关心知道具体产品类的类名,只需要知道所对应的工厂即可。

  1. 一个提供唯一对外接口的类通过其子类来指定创建哪个对象,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展;

  2. 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

小结

工厂方法模式和简单工厂模式的本直其实都是通过工厂来创建实际产品对象,但是他们又有所区别。

简单来说,他们之间最大的不同是——工厂方法模式在设计上完全完全符合开闭原则

相似之处在于简单工厂模式增加新产品时会增加工厂类中的判定模块以及具体产品类,工厂方法模式则在每次增加一个产品时,最坏的情况可能需要增加一个具体类和对应的工厂,这一步骤使得系统中类的个数成倍增加,增加了系统的复杂度。

那么有没有一种模式,可以解决工厂方法模式中这一隐患呢?这就不得不提到抽象工厂模式

03 | 抽象工厂模式

在这里插入图片描述

抽象工厂模式,顾名思义就是抽象 + 工厂的模式……
这一听起来,跟工厂方法有啥区别啊,工厂方法也是有一个抽象工厂基类啊,为啥还需要多整个抽象工厂模式咧?我知道你很急,但是你先别急
抽象工厂模式与工厂方法模式类似,但又有所差别,所涵盖的范围更广。工厂方法模式专注于把同一类别的事物抽象为一个工厂,而抽象工厂则是专注于把同一系列的事物抽象为一个工厂

框架

举个例子,假如小A想要将家里打造成网上很火的“智能家居”,那么理论上来说他要买的所有家居都应该是新型的智能家居,且应该是配套的,所以他选装的时候就得选择信任得品牌,比如选择“华为全屋智能1+2+N系统”。单独看选装的华为主机、华为的任一款智能家居都属于工厂方法模式中的一个工厂,那这就意味着设置了很多很多个独立的工厂。那么实际的华为工厂会是一个产品线一个厂吗?显然不是,而是把一系列相关的产品集成在一个厂中进行生产,这样的一系列产品也成为产品族群,这种将按照产品相关性划分,以产品族群为生产单位的工厂模式就称为抽象工厂模式

那么抽象工厂模式的框架应该如下(与工厂方法模式差不多,不同在于这里的一个具体工厂可以生产一系列的产品,而工厂方法模式中一个具体工厂只生产一种产品):

  1. 定义一个抽象工厂:用于提供实际工厂类的通用方法准则;

  2. 定义多个具体工厂:用于通知对应类别产品线;

  3. 定义一个抽象产品:同于提供实际产品线所通用的方法准则;

  4. 定义多个具体产品:用于进行实际产品的生产;

实现

假如现在有华为的家具系列(抽油烟机,电视)、照明系列(瓦斯警示灯, 吊灯),以这个基础通过C++实现的代码如下:

  • 抽象产品

    class furniture
    {public:furniture(){};virtual ~furniture(){};virtual void makeProduct() = 0;
    };class Light
    {public:Light(){};virtual ~Light(){};virtual void makeProduct() = 0;
    };
    
  • 抽象工厂

    class AbstractFactory
    {public:AbstractFactory(){};virtual ~AbstractFactory(){};virtual furniture* NotifyFurnitureProLine() = 0;virtual Light* NotifyLightProLine() = 0;
    };
    
  • 具体产品

    class Television : public furniture
    {public:Television(){};~Television(){};void makeProduct(){cout << "Television is finished!" << endl;}
    };class kitchenHood : public furniture
    {public:kitchenHood(){};~kitchenHood(){};void makeProduct(){cout << "kitchen hood is finished!" << endl;}
    };class Chandelier : public Light
    {public:Chandelier(){};~Chandelier(){};void makeProduct(){cout << "Chandelier is finished!" << endl;}
    };class GasWarningLight : public Light
    {public:GasWarningLight(){};~GasWarningLight(){};void makeProduct(){cout << "Gas Warning Light is finished!" << endl;}
    };
    
  • 具体工厂

    class DrawingRoomFactory : public AbstractFactory
    {public:DrawingRoomFactory(){cout << "Inform the production of drawing room" << endl;};~DrawingRoomFactory(){};furniture* NotifyFurnitureProLine(){return new Television;}Light* NotifyLightProLine(){return new Chandelier;}
    };class KitchenFactory : public AbstractFactory
    {public:KitchenFactory(){cout << "Notify the production of kitchen" << endl;};~KitchenFactory(){};furniture* NotifyFurnitureProLine(){return new kitchenHood;}Light* NotifyLightProLine(){return new GasWarningLight;}
    };
    
  • 测试

    int main()
    {AbstractFactory* fac = NULL;furniture* furn = NULL;Light* light = NULL;/* Drawing Room */fac = new DrawingRoomFactory();furn = fac->NotifyFurnitureProLine();light = fac->NotifyLightProLine();furn->makeProduct();light->makeProduct();delete fac, furn, light;cout << endl << "====================" << endl;/* Drawing Room */fac = new KitchenFactory();furn = fac->NotifyFurnitureProLine();light = fac->NotifyLightProLine();furn->makeProduct();light->makeProduct();delete fac, furn, light;return 0;
    }
    

抽象工厂模式测试结果

应用场景

  1. 软件系统中的类多个产品族群,每次只使用其中一个产品族群

  2. 同属于一个产品族群的产品在一起使用的软件系统

小结

抽象工厂模式,在我的理解相当于把工厂方法模式进行了分类抽象化,减少了工厂类的新增,但是系统结构如果发生修改,就需要对抽象层进行修改,这一改变其实是违背了面向对象设计的开闭原则的。

04 | 总结

在这里插入图片描述

三种工厂模式其实是一个逐步提升的过程

  1. 简单工厂模式到工厂方法模式:将创建对象的静态方法改为使用工厂方法,并提供一个统一的工厂来创建对应的产品对象的提升;

  2. 工厂方法模式到抽象工厂模式:将抽象工厂与具体工厂分离,每一个工厂不再只能生产单一的产品,而是可以生产一系列的产品,降低软件系统的类数量

总的来说,工厂模式初听时有点云里雾里,但是通过一步步的了解后,其实工厂模式我们早就接触过了,只是之前并不知道相关的概念,所以学无止尽就是这个意思吧……