本文将以 Objective-C 语言为媒介,介绍工厂设计模式的三大类型。
将于本文亮相的小朋友们
- xxFactory(各种工厂模式的工厂类)
- Product(抽象类)
- Product_subx(实体类,继承自 Product)
- AnotherProduct(抽象类 - 抽象工厂用到)
- AnotherProduct_subx(实体类 - 抽象工厂用到,继承自 AnotherProduct)
Simple Factory - 简单工厂
- 抽象概念:把生产所有产品的任务交给一个工厂。(这个工厂内部需要知道有哪些产品,以及所有产品的生产方式)
Example
在工厂 SimpleFactory 类的声明(.h)中可能(本文所有地方列举的数量仅作为样例说明)会包含两个产品的类型(比如用一个枚举:SimpleProductType),及一个公开的生成对象的接口。(假设有类方法接口 A:- (Product *)createSimpleProductWithType:(SimpleProductType)type;
)
1 | - SimpleFactory.h - |
而在 SimpleFactory 类的实现(.m)中,则会引入要生产的 Product1、Porduct2 类,并实现接口 A。(通过条件判断要创建哪种 Product 对象)
1 | - SimpleFactory.m - |
通过传参的形式创建对应的产品。
1 | Product *simple_product_1 = [SimpleFactory createSimpleProductWithType:SimpleProductType_1]; |
分析
使用场景
当对象较为简单,且工厂类负责创建的对象较少时。
如何实现
定义一个实体工厂类 SimpleFactory,在 SimpleFactory 中声明并实现创建所有产品 Product1、Product2 的方法。
优点
需求方只需调用工厂类的生产方法,无需关注具体的生产细节。
缺点
- 工厂内部的实现对于任何产品上的改动都是不封闭的。(改动一个产品的生产逻辑就会对整体的产品创建逻辑进行改动)
- 它集中了所有产品创建的逻辑,随着产品数量的增加或创建形式的改动,工厂类的可拓展性和可维护性将愈来愈差。
Facroty Method - 工厂方法
- 抽象概念:把生产一种产品的工作交给对应的一个工厂。(有多少种产品就有多少种负责生产对应产品的工厂,一个工厂也只能生产一种产品)
Example
首先,有一个工厂的抽象类 FactoryMethod,它声明了一个创建产品的接口。(假设为:+ (id)createMethodProduct;
)
1 | - FactoryMethod.h - |
然后,有两个实体类 FactoryMethod_sub1、FactoryMethod_sub2 继承自 FactoryMethod,引入对应的产品类 Product_subx 并实现创建产品的接口。
1 | - FactoryMethod_sub1.h - |
1 | - FactoryMethod_sub2.h - |
最后,通过相应的工厂类创建产品。
1 | Product *method_product_1 = [FactoryMethod_sub1 createMethodProduct]; |
分析
使用场景
当需要创建比较复杂的对象时。
如何实现
定义抽象类 FactoryMethod,并定义产品的创建方法。定义产品对应的工厂类 FactoryMethod_sub1、FactoryMethod_sub2 等继承自 FactoryMethod,引入对应的产品并实现产品的创建方法。
优点
- 符合面向对象设计中的单一职责原则,程序的可拓展性很强。
- 各个工厂之间相互独立,任何改动都不会影响其他产品的创建,程序的稳定性和可维护性很强。
缺点
通过子类创建,创建时需指定具体的类,入口不统一,所以使得调用方很不方便。
Abstract Factory - 抽象工厂
为了更方便地理解抽象工厂,引入:另一种产品 AnotherProduct 的概念。
- 抽象概念:一个工厂可以生产多种该工厂所能生产的产品。(一个被服厂除了能生产床单,还可以生产被罩、窗帘等等。当然,可能有多个工厂)
☆ | Product_subx | AnotherProduct_subx |
---|---|---|
Factory_subx | ✔️ | ✔️ |
理解上图为多种产品(横轴) + 工厂(纵轴)的交叉。
Example
首先,有一个工厂的抽象类 AbstractFactory,它的声明中包含两个产品总共四个类型( AbstractProductType 2 + AnotherAbstractProductType 2)及两个产品的创建接口。
1 | - AbstractFactory.h - |
而在它的实现中,有着类似于普通工厂的结构。(按条件创建产品)
1 | - AbstractFactory.m |
到此,借着上文工厂方法的思想,可以大致确定接下来的思路:再创建两个 AbstractFactory 的子类 AbstractFactory_sub1 和 AbstractFactory_sub2,然后分别实现各自对应的两种产品的创建方法。
但是。
仔细观察上面代码就会发现,AbstractFactory 类的声明中并没有包含其实现中子类所调用的 createProduct 及 createAnotherProduct 这两个类方法,也就是说,与工厂方法一节不同的是,子类并没有继承这两个方法。那么是否意味着需要子类各自编写相同类型产品的创建方法?如果是,岂不是完全没有继承的意义?
当然不是。
那么问题来了:为什么不在父类 AbstractFactory 中写那两个创建方法来让子类继承?
因为 AbstractFactory 是个抽象类,对它而言,所谓的创建实体,是没有意义的。我们要的效果,是通过调用这个抽象类的方法,来创建我们想要的各种产品,而创建对应类型的产品,就应该由对应的工厂(AbstractFactory 的子类)来实现。所以,如果把创建方法写在父类的声明里,这两个接口就会暴露出去,需求方在调用的时候,就会产生困惑,因为调用方并不知道这两个接口是干什么的(创建方法此时应隐藏于内部),也不知道工厂 AbstractFactory 对应的产品 Product 是什么。(因为它们都是抽象的概念,你可以要“篮球鞋”,也可以要“足球鞋”,但你不能要“鞋”)
那么问题又来了:如何在隐藏继承关系中的私有接口的同时,也要让继承的概念有意义呢?
可以利用 OOP 的性质,对父类 AbstractFactory 进行拓展,将这两个接口放入其中。
1 | - AbstractFactory+Super.h - |
如此一来,问题就解决了。下面只需要在父类及子类的实现里引入这份拓展,并按照上文提到的接下来的思路进行就好了。
1 | Product *abstract_product_1 = [AbstractFactory createAbstractProductWithType:AbstractProductType_1]; |
分析
使用场景
当需要创建一系列相互关联的对象时。(肉场可成产牛肉、羊肉、鸡肉等)
如何实现
定义抽象类 AbstractFactory,并对其进行拓展,其中包含了各种产品的创建方法。定义各种产品对应的工厂类 AbstractFactory_sub1、AbstractFactory_sub2 等继承自 AbstractFactory,引入对应的产品和父类的拓展并实现产品的创建方法。
优点
- 可以完全忽略工厂的内部差异性,对调用方而言,只需了解公共接口就行,极大降低了调用成本。
- 程序具有比使用工厂方法更加强大的稳定性和可拓展性。
缺点
如果面对的业务错综复杂,产品的新增或修改导致整个产品等级结构有影响的话,则对开发者的能力有一定要求。
结语
到这里,工厂设计模式就告一段落了。
可以发现,简单工厂 -> 工厂方法 -> 抽象工厂 这条链,是层层递进的关系。每一级都可以弥补上一级的不足。但相对的,越高级的设计模式,其代码结构就越复杂,其维护成本就越高。对于开发者而言,选择合适于当前应用场景的,才是最好的。