iOS设计模式 - 工厂方法模式

iOS设计模式 - 工厂方法模式

引入

前面我们已经介绍过 简单工厂模式 的基本概念,知道了它的特点是将对象的创建与使用进行了分离,屏蔽了对象创建的内部细节,让外部的调用更加方便。但是它的的缺点也比较明显,当需要添加新的目标对象时,我们不得不修改工厂类提供的静态方法,如果目标对象过多,工厂类就会比较臃肿,不利于项目的拓展和维护,同时也违背了 开闭原则 。为了解决这个问题,就需要引入一个新的设计模式 — 工厂方法模式

博文中引用的 Demo 已经放到 github 上面了,有需要的小伙伴可以去下载

模式介绍

工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。
它是对 简单工厂模式 的进一步抽象,将原先的核心工厂类拆分为抽象工厂父类以及其派生的工厂子类。工厂父类负责定义创建目标对象的公共接口,而工厂子类则负责生成具体的目标对象。这样做的目的是将目标类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体目标类。

模式结构

工厂方法模式主要包含以下四个角色:

  1. AbstractFactory

    抽象工厂,负责定义创建目标对象的公共接口。

  2. ConcreteFactory

    具体工厂,负责创建具体目标对象的实例。

  3. AbstractTarget

    抽象目标,目标对象的父类(接口),负责定义所有目标对象的公共属性和方法。

  4. ConcreteTarget

    具体目标,被创建的实例对象,是 AbstractTarget 的子类。这里泛指具有一系列相同特性的类。


代码分析

抽象目标类

所有被创建的目标实例的父类,用来声明一些共有的属性和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
// AbstractTarget.h
//

#import <Foundation/Foundation.h>

@protocol AbstractTarget <NSObject>

@required
/**
* 声明一个公共的接口,遵守了协议的类去实现它
*/
- (void)doSomething;

@end

抽象工厂类

所有被创建的工厂的父类,定义了创建目标对象的接口,由具体子类工厂来实现创建目标对象的代码逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//
// AbstractFactory.h
//

#import <Foundation/Foundation.h>
#import "AbstractTarget.h"

@protocol AbstractFactory <NSObject>

@required
/**
* 定义一个返回 实现 AbstractTarget 协议对象的接口
* 所有工厂子类必须实现这个接口,并在内部实现创建目标对象的逻辑
*/
- (id<AbstractTarget>)getTarget;

@end

具体目标类

实现了抽象目标类里面声明的方法,将来会被对应的工厂类实例化。

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

#import <Foundation/Foundation.h>
#import "AbstractTarget.h"
#import "TargetA.h"

@interface TargetA ()<AbstractTarget>
@end

@implementation TargetA

#pragma mark - 💉 👀 AbstractTarget 👀

- (void)doSomething
{
NSLog(@"TargetA 的 doSomething 被调用");
}

@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
//
// FactoryA.m
//

#import <Foundation/Foundation.h>
#import "AbstractFactory.h"
#import "FactoryA.h"
#import "TargetA.h"

@interface FactoryA ()<AbstractFactory>
@end

@implementation FactoryA

#pragma mark - 💉 👀 AbstractFactory 👀

/**
* 创建目标对象 TargetA 的实例
*/
- (id<AbstractTarget>)getTarget
{
return [TargetA new];
}

@end

外部使用

当需要使用目标对象时,先实例化一个对应的工厂对象,然后通过这个工厂实例来获取目标对象。以后如果需要引入新的目标对象,只需再添加一个新的工厂类,让这个工厂来负责新目标对象的创建,这样就不需要修改之前的代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#import "FactoryA.h"

- (void)test
{
/// 创建具体工厂
id<AbstractFactory> factory = [FactoryA new];
/// 通过工厂对象来获取具体的目标对象
id<AbstractTarget> target = [factory getTarget];
/// 消费实例(调用目标实例的方法)
[target doSomething];
}

@end

总结

优点
  1. 将对象的创建与使用进行了分离,在一定程度上降低了系统的耦合度。

  2. 屏蔽了对象创建的过程,外部只需要使用对象,无须关心对象创建的具体细节。

  3. 将目标对象的创建交给了具体的工厂类来负责,抽象的工厂类只负责定义创建目标对象的公共接口。这种抽象化的结果使这种结构可以在不修改原有工厂类的情况下引进新的目标对象,代码的可扩展性也就变得非常好,更加符合 开闭原则

缺点
  1. 在添加新的产品时,需要编写新的目标对象类,还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,而且有更多的类需要编译和运行,会给系统带来一些额外的开销。