上篇已经介绍了适配器模式、桥接模式和组合模式,这篇将介绍装饰者模式、外观模式、享元模式和代理模式。
装饰者(Decorator)
装饰者模式可以动态地给一个对象添加一些额外的职责。
举个例子,我们要给UIView
及其子类创建一个装饰者,在调用addSubview
方法的时候打印一条调试信息:
class LogDecorator: UIView {
var view: UIView
init(frame: CGRect, view: UIView) {
self.view = view
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func addSubview(view: UIView) {
self.view.addSubview(view)
log()
}
func log() {
// ...
print("Add a subview.")
}
}
LogDecorator
继承自UIView
,在能够使用UIView
的地方也同样可以使用LogDecorator
。这个装饰者可以用来装饰UIView
及其所有子类,譬如装饰一个 Button:
let button = LogDecorator(frame: frame, view: UIButton())
button.addSubview(UIView())
//打印信息
Add a subview.
装饰者模式跟对象适配器模式很像,但是装饰者跟被装饰者必须是继承自同一个抽象类的,对外提供一致的接口;而适配器跟被适配者却没有这个限制。虽然适配器也可以给被适配者增加新的职责,扩展它的功能,但是它们的目的是不同的,前者是为了动态地给某个对象增添新功能,而后者则是为了包装已有对象,对外提供符合需求的接口。
外观(Facade)
外观模式为子系统中的一组接口提供一个一致的界面。
这个呢其实没什么可多说的,无非是新建一个类用作用户界面,将一个复杂子系统中的类组合起来,对外提供一个易用的高层接口,隐藏内部细节。这里蕴含分层的思想,可以让系统模块化程度更高,便于复用,也便于使用。
享元(Flyweight)
享元模式运用共享技术有效地支持大量细粒度的对象。
Flyweight 是一个共享对象,它包含内部状态和外部状态,内部状态是那些可以用来共享的状态,而外部状态则是需要根据不同场景进行计算得到的状态。
有些做 iOS 开发的同学可能对享元模式这个名字比较陌生,但其实我们几乎天天都要跟它打交道。比如 TableView 和 CollectionView 中 Cell 的重用机制,就是运用享元模式的一大典范。Cell 对象就是一个 Flyweight,Cell 包含的那些 Subview(以及Subview 的位置大小颜色等信息)都是内部状态,而 Cell 的高度、要显示的内容等等,这些都是外部状态,是需要在 Cell 显示之前计算得到的。说到这里想必大家也明白享元模式的作用了,对的,就是为了节约内存。
代理(Proxy)
代理模式为其他对象提供一种代理以控制对这个对象的访问
代理模式在形式上其实跟装饰者模式是差不多的,代理者跟实际对象都继承自同一个抽象类,代理者持有一个指向实际对象的指针。使用时可以用代理对象代替实际对象,代理对象控制对实际对象的存取,并可能负责创建和删除它,其他附加功能根据代理的类型而有所不同。
代理一般分为以下几种类型:
- 远程代理(Remote Proxy):负责对请求及其参数进行编码,并向不同地址空间的实体对象发送已编码的请求。
- 虚代理(Virtual Proxy):缓存实体的附加信息,实现延迟加载(Lazy Load)等功能。
- 保护代理(Protection Proxy):检查调用者是否拥有对实体的访问权限,并分情况进行处理。
- 智能指引(Smart Reference):取代简单的指针,在访问对象时执行一些附加操作(控制引用计数、首次加载持久对象、加锁保证线程安全等)。
由此可见,iOS 开发中无处不在的 Delegate(委托)其实跟代理模式是有区别的,委托对象跟实际对象并没有一致的接口,只是在某些特定的时间节点调用委托对象中的方法(一般以对应实际对象为参数),从而对实际对象进行操作。
小结
到此为止结构型模式就介绍完了,想必大家也发现了,其实绕来绕去就是类继承跟对象组合罢了,只是因为设计目的不同以及一些实现上的细微差别,才分出了这么多模式。
设计模式就像公式定理,你可以把它背下来,这样在跟人交流或者阅读别人的系统的时候,会少很多障碍。但比公式更重要的是推导过程,对应到日常开发中就是系统设计能力。套用公式并不能解决所有问题,所以大家在学习设计模式的时候还是要多学习它的设计思路,知道每个模式是针对什么场景设计的,这样设计的好处与弊端,它具体是怎么实现的,场景变化的时候可以做怎样的变通,等等。只有这样,你才能真正从设计模式中受益。