单例模式
定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
类图
单例类(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
14POPAnimatableProperty *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 处理。
效果
- 改变一个产品的内部表示时,所要做的只是定义一个新的生成器。
- 将构造代码和表现代码分离。
- 对构造过程进行更精细的控制。