工厂设计模式(Objective-C)【Factory Design Pattern】

本文将以 Objective-C 语言为媒介,介绍工厂设计模式的三大类型。

Demo

将于本文亮相的小朋友们

  • xxFactory(各种工厂模式的工厂类)
  • Product(抽象类)
  • Product_subx(实体类,继承自 Product)
  • AnotherProduct(抽象类 - 抽象工厂用到)
  • AnotherProduct_subx(实体类 - 抽象工厂用到,继承自 AnotherProduct)

Simple Factory - 简单工厂

  • 抽象概念:把生产所有产品的任务交给一个工厂。(这个工厂内部需要知道有哪些产品,以及所有产品的生产方式)

Example

在工厂 SimpleFactory 类的声明(.h)中可能(本文所有地方列举的数量仅作为样例说明)会包含两个产品的类型(比如用一个枚举:SimpleProductType),及一个公开的生成对象的接口。(假设有类方法接口 A:- (Product *)createSimpleProductWithType:(SimpleProductType)type;

1
2
3
4
5
6
7
8
9
10
11
12
13
- SimpleFactory.h -


typedef NS_ENUM(NSUInteger, SimpleProductType) {
SimpleProductType_1,
SimpleProductType_2
};

@interface SimpleFactory : NSObject

+ (Product *)createSimpleProductWithType:(SimpleProductType)type;

@end

而在 SimpleFactory 类的实现(.m)中,则会引入要生产的 Product1、Porduct2 类,并实现接口 A。(通过条件判断要创建哪种 Product 对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- SimpleFactory.m -


#import "SimpleFactory.h"
#import "Product_sub1.h"
#import "Product_sub2.h"

@implementation SimpleFactory

+ (Product *)createSimpleProductWithType:(SimpleProductType)type {
switch (type) {
case SimpleProductType_1:
return [[[Product_sub1 alloc] init] create];
break;
case SimpleProductType_2:
return [[[Product_sub2 alloc] init] create];
break;
}
}

@end

通过传参的形式创建对应的产品。

1
2
3
4
5
6
7
8
Product *simple_product_1 = [SimpleFactory createSimpleProductWithType:SimpleProductType_1];
NSLog(@"SimpleFactory:%@ - %@ \n", simple_product_1.name, [simple_product_1 class]);
Product *simple_product_2 = [SimpleFactory createSimpleProductWithType:SimpleProductType_2];
NSLog(@"SimpleFactory:%@ - %@ \n", simple_product_2.name, [simple_product_2 class]);

Result:
2018-10-28 14:12:47.620257+0800 FactoryDesignPattern[17103:2145157] SimpleFactory:sub1 - Product_sub1
2018-10-28 14:12:47.620435+0800 FactoryDesignPattern[17103:2145157] SimpleFactory:sub2 - Product_sub2

分析

  1. 使用场景

    当对象较为简单,且工厂类负责创建的对象较少时。

  2. 如何实现

    定义一个实体工厂类 SimpleFactory,在 SimpleFactory 中声明并实现创建所有产品 Product1、Product2 的方法。

  3. 优点

    需求方只需调用工厂类的生产方法,无需关注具体的生产细节。

  4. 缺点

    • 工厂内部的实现对于任何产品上的改动都是不封闭的。(改动一个产品的生产逻辑就会对整体的产品创建逻辑进行改动)
    • 它集中了所有产品创建的逻辑,随着产品数量的增加或创建形式的改动,工厂类的可拓展性和可维护性将愈来愈差。

Facroty Method - 工厂方法

  • 抽象概念:把生产一种产品的工作交给对应的一个工厂。(有多少种产品就有多少种负责生产对应产品的工厂,一个工厂也只能生产一种产品)

Example

首先,有一个工厂的抽象类 FactoryMethod,它声明了一个创建产品的接口。(假设为:+ (id)createMethodProduct;

1
2
3
4
5
6
7
8
9
10
- FactoryMethod.h -


#import <Foundation/Foundation.h>

@interface FactoryMethod : NSObject

+ (id)createMethodProduct;

@end

然后,有两个实体类 FactoryMethod_sub1、FactoryMethod_sub2 继承自 FactoryMethod,引入对应的产品类 Product_subx 并实现创建产品的接口。

1
2
3
4
5
6
7
8
9
10
11
12
- FactoryMethod_sub1.h -

#import "FactoryMethod_sub1.h"
#import "Product_sub1.h"

@implementation FactoryMethod_sub1

+ (id)createMethodProduct {
return [[[Product_sub1 alloc] init] create];
}

@end
1
2
3
4
5
6
7
8
9
10
11
12
- FactoryMethod_sub2.h -

#import "FactoryMethod_sub2.h"
#import "Product_sub2.h"

@implementation FactoryMethod_sub2

+ (id)createMethodProduct {
return [[[Product_sub2 alloc] init] create];
}

@end

最后,通过相应的工厂类创建产品。

1
2
3
4
5
6
7
8
Product *method_product_1 = [FactoryMethod_sub1 createMethodProduct];
NSLog(@"FactoryMethod_1:%@ - %@ \n", method_product_1.name, [method_product_1 class]);
Product *method_product_2 = [FactoryMethod_sub2 createMethodProduct];
NSLog(@"FactoryMethod_2:%@ - %@ \n", method_product_2.name, [method_product_2 class]);

Result:
2018-10-28 14:12:47.620526+0800 FactoryDesignPattern[17103:2145157] FactoryMethod_1:sub1 - Product_sub1
2018-10-28 14:12:47.620619+0800 FactoryDesignPattern[17103:2145157] FactoryMethod_2:sub2 - Product_sub2

分析

  1. 使用场景

    当需要创建比较复杂的对象时。

  2. 如何实现

    定义抽象类 FactoryMethod,并定义产品的创建方法。定义产品对应的工厂类 FactoryMethod_sub1、FactoryMethod_sub2 等继承自 FactoryMethod,引入对应的产品并实现产品的创建方法。

  3. 优点

    • 符合面向对象设计中的单一职责原则,程序的可拓展性很强。
    • 各个工厂之间相互独立,任何改动都不会影响其他产品的创建,程序的稳定性和可维护性很强。
  4. 缺点

    通过子类创建,创建时需指定具体的类,入口不统一,所以使得调用方很不方便。

Abstract Factory - 抽象工厂

为了更方便地理解抽象工厂,引入:另一种产品 AnotherProduct 的概念。

  • 抽象概念:一个工厂可以生产多种该工厂所能生产的产品。(一个被服厂除了能生产床单,还可以生产被罩、窗帘等等。当然,可能有多个工厂)
Product_subx AnotherProduct_subx
Factory_subx ✔️ ✔️

理解上图为多种产品(横轴) + 工厂(纵轴)的交叉。

Example

首先,有一个工厂的抽象类 AbstractFactory,它的声明中包含两个产品总共四个类型( AbstractProductType 2 + AnotherAbstractProductType 2)及两个产品的创建接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- AbstractFactory.h -


#import <Foundation/Foundation.h>
#import "Product.h"
#import "AnotherProduct.h"

typedef NS_ENUM(NSUInteger, AbstractProductType) {
AbstractProductType_1,
AbstractProductType_2
};

typedef NS_ENUM(NSUInteger, AnotherAbstractProductType) {
AnotherAbstractProductType_1,
AnotherAbstractProductType_2
};

@interface AbstractFactory : NSObject

+ (Product *)createAbstractProductWithType:(AbstractProductType)type;

+ (AnotherProduct *)createAnotherAbstractProductWithType:(AnotherAbstractProductType)type;

@end

而在它的实现中,有着类似于普通工厂的结构。(按条件创建产品)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
- AbstractFactory.m


#import "AbstractFactory.h"
#import "AbstractFactory+Super.h"
#import "AbstractFactory_sub1.h"
#import "AbstractFactory_sub2.h"

@implementation AbstractFactory

+ (Product *)createAbstractProductWithType:(AbstractProductType)type {
switch (type) {
case AbstractProductType_1:
return [AbstractFactory_sub1 createProduct];
break;
case AbstractProductType_2:
return [AbstractFactory_sub2 createProduct];
break;
}
}

+ (AnotherProduct *)createAnotherAbstractProductWithType:(AnotherAbstractProductType)type {
switch (type) {
case AnotherAbstractProductType_1:
return [AbstractFactory_sub1 createAnotherProduct];
break;
case AnotherAbstractProductType_2:
return [AbstractFactory_sub2 createAnotherProduct];
default:
break;
}
}

+ (id)createProduct {
return nil;
}

+ (id)createAnotherProduct {
return nil;
}

@end

到此,借着上文工厂方法的思想,可以大致确定接下来的思路:再创建两个 AbstractFactory 的子类 AbstractFactory_sub1 和 AbstractFactory_sub2,然后分别实现各自对应的两种产品的创建方法。

但是。

仔细观察上面代码就会发现,AbstractFactory 类的声明中并没有包含其实现中子类所调用的 createProduct 及 createAnotherProduct 这两个类方法,也就是说,与工厂方法一节不同的是,子类并没有继承这两个方法。那么是否意味着需要子类各自编写相同类型产品的创建方法?如果是,岂不是完全没有继承的意义?

当然不是。

那么问题来了:为什么不在父类 AbstractFactory 中写那两个创建方法来让子类继承?

因为 AbstractFactory 是个抽象类,对它而言,所谓的创建实体,是没有意义的。我们要的效果,是通过调用这个抽象类的方法,来创建我们想要的各种产品,而创建对应类型的产品,就应该由对应的工厂(AbstractFactory 的子类)来实现。所以,如果把创建方法写在父类的声明里,这两个接口就会暴露出去,需求方在调用的时候,就会产生困惑,因为调用方并不知道这两个接口是干什么的(创建方法此时应隐藏于内部),也不知道工厂 AbstractFactory 对应的产品 Product 是什么。(因为它们都是抽象的概念,你可以要“篮球鞋”,也可以要“足球鞋”,但你不能要“鞋”)

那么问题又来了:如何在隐藏继承关系中的私有接口的同时,也要让继承的概念有意义呢?

可以利用 OOP 的性质,对父类 AbstractFactory 进行拓展,将这两个接口放入其中。

1
2
3
4
5
6
7
8
9
- AbstractFactory+Super.h -	


@interface AbstractFactory()

+ (id)createProduct;
+ (id)createAnotherProduct;

@end

如此一来,问题就解决了。下面只需要在父类及子类的实现里引入这份拓展,并按照上文提到的接下来的思路进行就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Product *abstract_product_1 = [AbstractFactory createAbstractProductWithType:AbstractProductType_1];
NSLog(@"AbstractFactory_1:%@ - %@ \n", abstract_product_1.name, [abstract_product_1 class]);
Product *abstract_product_2 = [AbstractFactory createAbstractProductWithType:AbstractProductType_2];
NSLog(@"AbstractFactory_2:%@ - %@ \n", abstract_product_2.name, [abstract_product_2 class]);
AnotherProduct *abstract_another_product_1 = [AbstractFactory createAnotherAbstractProductWithType:AnotherAbstractProductType_1];
NSLog(@"AbstractFactory_1:%@ - %@ \n", abstract_another_product_1.name, [abstract_another_product_1 class]);
AnotherProduct *abstract_another_product_2 = [AbstractFactory createAnotherAbstractProductWithType:AnotherAbstractProductType_2];
NSLog(@"AbstractFactory_2:%@ - %@ \n", abstract_another_product_2.name, [abstract_another_product_2 class]);

Result:
2018-10-28 17:06:44.959904+0800 FactoryDesignPattern[17659:2367577] AbstractFactory_1:sub1 - Product_sub1
2018-10-28 17:06:44.960001+0800 FactoryDesignPattern[17659:2367577] AbstractFactory_2:sub2 - Product_sub2
2018-10-28 17:06:44.960176+0800 FactoryDesignPattern[17659:2367577] AbstractFactory_1:another_sub1 - AnotherProduct_sub1
2018-10-28 17:06:44.960287+0800 FactoryDesignPattern[17659:2367577] AbstractFactory_1:another_sub2 - AnotherProduct_sub2

分析

  1. 使用场景

    当需要创建一系列相互关联的对象时。(肉场可成产牛肉、羊肉、鸡肉等)

  2. 如何实现

    定义抽象类 AbstractFactory,并对其进行拓展,其中包含了各种产品的创建方法。定义各种产品对应的工厂类 AbstractFactory_sub1、AbstractFactory_sub2 等继承自 AbstractFactory,引入对应的产品和父类的拓展并实现产品的创建方法。

  3. 优点

    • 可以完全忽略工厂的内部差异性,对调用方而言,只需了解公共接口就行,极大降低了调用成本。
    • 程序具有比使用工厂方法更加强大的稳定性和可拓展性。
  4. 缺点

    如果面对的业务错综复杂,产品的新增或修改导致整个产品等级结构有影响的话,则对开发者的能力有一定要求。

结语

到这里,工厂设计模式就告一段落了。
可以发现,简单工厂 -> 工厂方法 -> 抽象工厂 这条链,是层层递进的关系。每一级都可以弥补上一级的不足。但相对的,越高级的设计模式,其代码结构就越复杂,其维护成本就越高。对于开发者而言,选择合适于当前应用场景的,才是最好的。