心辰·Dev

iOS 开发常见设计模式解析 | 创建型模式

单例模式

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

类图

单例
单例类(Singleton)定义一个实例化类操作,允许客户通过该操作访问唯一实例。

实践

Cocoa 框架中,UIApplication / UIAccelerometer / NSFileManager 等类均实现为单例类。通过 GCD 多线程框架可以方便的创建一个单例:

1
2
3
4
5
6
7
8
9
10
+ (instancetype)sharedinstance {
static dispatch_once_t oncetoken;
static SingletonClass *sharedinstance;
dispatch_once(&oncetoken, ^{
if (!sharedinstance){
sharedinstance = [[SingletonClass alloc] init];
}
});
return sharedinstance;
}

效果

  • 客户何时及怎样访问该单例将被控制。
  • 是对全局变量的一个改进。

原型模式

定义

用原型实例指定创建对象的种类,并通过拷贝这个原型来创建新的对象。

类图

原型
客户让一个原型克隆自身从而创建一个新对象,原型(Prototype)声明一个克隆自身接口,具体原型(ConcretePrototype)实现克隆自身操作。

实践

iOS 编程中,经常涉及深拷贝和浅拷贝的概念。浅拷贝时,拷贝中的成员变量和原型中的成员是共享指针的,而实现深拷贝,则还要依次克隆对象的实例变量。使 NSObject 的子类实现深拷贝,先遵从<NSCopying>协议,再实现copyWithZone方法,如AFNetworking框架中,所有类都实现了<NSCopying>中的协议方法,如:

1
2
3
4
5
6
7
8
9
- (instancetype)copyWithZone:(NSZone *)zone {
AFMultipartBodyStream *bodyStreamCopy = [[[self class] allocWithZone: zone]
initWithStringEncoding: self.stringEncoding];
for (AFHTTPBodyPart *bodyPart in self.HTTPBodyParts) {
[bodyStreamCopy appendHTTPBodyPart: [bodyPart copy]];
}
[bodyStreamCopy setInitialAndFinalBoundaries];
return bodyStreamCopy;
}

以上的实现将 AFMultipartBodyStream 类中的HTTPBodyParts成员也进行了拷贝。

效果

  • 运行时增加或删除产品原型。
  • 实现为浅拷贝,改变值以指定一个新的对象。
  • 实现为深拷贝,改变结构以指定一个新的对象。

工厂方法

定义

定义一个用于创建对象的接口,让子类决定将哪一个类实例化,使一个类的实例化延迟到其子类。

类图

工厂方法
当类无法决定或希望子类决定创建何种对象的时候,使用该模式。抽象创建者(Creator)声明工厂方法,返回一个产品(Product)类型的对象。具体创建者继承抽象创建者,重写工厂方法以返回一个具体产品实例。工厂方法创建的对象可以拥有一组共同的行为。

实践

以 NSNumber 为例,它的类方法[NSNumber numberWithBool: Yes]是一个类工厂方法,会返回 NSNumber 的子类 NSCFBoolean 的一个实例;[NSNumber numberWithFloat: .2]会得到 NSCFNumber 的一个实例。这些子类有共同的行为接口如-intValue / -boolValue等。

效果

  • 工厂方法不再与特定应用有关的类绑定。

抽象工厂

定义

提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类。

类图

抽象工厂
抽象工厂(AbstractFactory)可以是父类,也可以是协议,它们定义了工厂方法。创建多系列产品时,需要多个不同子类具体工厂(ConcreteFactory),在子类工厂中覆写并实现抽象工厂定义的工厂方法,以生产不同的元素(ProductA / ProductB)。

实践

该模式在 Cocoa 框架中被实现为类簇,有很多基础类如 NSData / NSString / NSArray / NSDictionary / NSNumber 等均按该模式设计。 如 NSNumber 类本身是抽象工厂,NSCFBoolean / NSCFNumber 是具体工厂子类。-intValue-boolValue方法就是根据实际对象返回内部值的工厂方法。

效果

  • 分离了具体的类,将创建对象的责任和过程进行封装,与客户类分离。
  • 利于产品一致性,通过一致的接口得到同一系列的对象。
  • 灵活转变产品,只需改变具体工厂,即可使用不同的产品配置。
  • 难以支持新种类产品,难以拓展抽象工厂接口。

生成器模式

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

类图

生成器
该模式把构建对象涉及的类分解成:客户-指导者-生成器(Client-Director-Builder),把构建中”what”和”how”分离。指导者(Director)类仅仅定义构建何种对象的方法(what),通过把建造请求发送给生成器(Builder),而把具体的逐步构建过程和细节(how)交给生成器。

实践

在 Facebook 的 Pop 动画框架中,创建一个可动画属性的过程如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POPAnimatableProperty *prop = [POPAnimatableProperty propertyWithName: @"com.foo.radio.volume"
initializer: ^(POPMutableAnimatableProperty *prop) {
// read value
prop.readBlock = ^(id obj, CGFloat values[]) {
values[0] = [obj volume];
};
// write value
prop.writeBlock = ^(id obj, const CGFloat values[]) {
[obj setVolume: values[0]];
};
// dynamics threshold
prop.threshold = 0.01;
}];
anim.property = prop;

这里创建POPAnimatableProperty的方法,参数 initializer 即一个生成器(builder),把逐步构建复杂对象的过程交给 Block 处理。

效果

  • 改变一个产品的内部表示时,所要做的只是定义一个新的生成器。
  • 将构造代码和表现代码分离。
  • 对构造过程进行更精细的控制。