iOS设计模式 - 简单工厂模式

iOS设计模式 - 简单工厂模式

前言

在实际的开发过程中,我们常常会遇到这样一种场景,项目中需要用到一系列具有相同特性的类,如一组用来提示消息的弹出框视图,它们可能会有多个不同的样式(成功的弹框、失败的弹框、警告的弹框等)。这些提示框都源自同一个父类,而各自的内部又都重写了父类的部分属性使得它们有不同的表现形式。如果我们希望在使用这些提示框时,不需要关心它们是如何创建的,甚至是不需要知道它们的类名,只需要知道表示该提示框类的一个参数,并提供一个调用方便的方法,把该参数传入方法即可返回一个相应的提示框对象。此时,就可以使用 简单工厂模式

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

模式介绍

简单工厂模式(Simple Factory Pattern)又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。
简单工厂模式是基于 OOP 中的 多态 来实现的,它需要定义一个工厂类来负责创建其它类的实例,而被创建的实例通常都具有共同的父类或者实现了相同的的协议(接口)。
通过使用简单工厂模式,我们可以根据不同的参数来获取不同类的实例。

模式结构

简单工厂模式主要包含以下三个角色:

  1. Factory

    核心工厂,负责实现创建所有实例的内部逻辑。

  2. AbstractTarget

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

  3. 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
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
//
// Factory.h
//

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

/// 目标类型
typedef NS_ENUM(NSUInteger, TargetType) {
/// 目标 A
TargetTypeA = 0,
/// 目标 B
TargetTypeB
};

@interface Factory : NSObject

/**
* 根据 type 值来获取我们想要的目标实例
*
* @param type 目标实例的类型
*/
+ (id<AbstractTarget>)getTarget:(TargetType)type;

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

#import "Factory.h"
#import "TargetA.h"
#import "TargetB.h"

@implementation Factory

+ (id<AbstractTarget>)getTarget:(TargetType)type
{
switch (type){
case TargetTypeA:
return [TargetA new]; // 返回目标A的实例
case TargetTypeB:
return [TargetB new]; // 返回目标B的实例
}
}

@end

外部使用

当需要使用目标对象时,可以通过工厂类来获取。通过调用工厂类的静态方法并传入具体的 type 值来得到目标对象的实例。

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

#import "Factory.h"

- (void)test
{
/// 调用工厂类的静态方法,根据类型来获取指定的目标实例
id<AbstractTarget> target = [Factory getTarget:TargetTypeA];
/// 消费实例(调用目标实例的方法)
[target doSomething];
}

@end

总结

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

  2. 工厂类提供的方法是静态方法,可通过类名直接调用,使用起来很方便。

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

缺点
  1. 工厂类不够灵活,增加新的目标类时需要修改工厂类的判断逻辑代码,当目标类较多时,工厂方法代码将会过于复杂,不利于项目的扩展和维护。