GoF 归纳整理的23个设计模式依据其目的可以分为创建型(Creational)、结构型(Structural)和行为型(Behavioral)三种。关于创建型模式上次我已经写过一篇文章了,这次就说一说结构型模式。
顾名思义,结构型模式的目的主要就是组合类和对象以获得一个更大更合适的结构,具体包括适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式和代理模式。本文主要介绍前三种模式,剩余的留待下期再论。
适配器(Adapter)
适配器模式的目的是在不改变已有类(被适配者)的前提下,用一个新类(适配器)去继承或者组合它,然后对外提供符合需求的接口。
设计模式的分类遵从目的准则和范围准则,像创建型、结构型、行为型这样的分法是基于目的准则的,而像类模式、对象模式这样的分法则是基于范围准则。类模式是指那些主要针对类,使用继承来实现的模式;对象模式是指那些主要使用对象组合来实现的模式。对象模式可以在运行期改变对象组合,所以比类模式更灵活更动态。适配器模式可以通过多继承和对象组合实现,所以说适配器模式既可以是类模式,也可以是对象模式。
以 Swift 举个例子(Swift 不支持多继承,但支持实现多个协议):
protocol Animal {
//...
func move()
}
class Bird {
//...
func fly() {
print("Bird is flying.")
}
}
func playWithAnimal(animal: Animal) {
print("----Play----")
animal.move()
}
假设Bird
是一个早已存在的类,我们现在想把让它用到playWithAnimal
这个方法里去,但显然是不能直接用的,当然也不能轻易去修改它,因为它可能跟很多其他的类有关联,一旦轻易修改就可能造成连锁 Bug。这个时候就可以让适配器模式来一展拳脚了。
首先,我们使用类适配器,新建一个BirdAdapter
继承Bird
:
class BirdClassAdapter: Bird, Animal {
func move() {
fly()
}
}
let bird = BirdClassAdapter()
playWithAnimal(bird)
这样就可以正常调用了,而且不会影响到原有的类。
接下来我们使用对象适配器,新建一个BirdAdapter
来组合Bird
对象:
class BirdObjectAdapter: Animal {
var bird: Bird
init(bird: Bird) {
self.bird = bird
}
func move() {
bird.fly()
}
}
let bird = BirdObjectAdapter(bird: Bird())
playWithAnimal(bird)
类适配器可以在适配器中重写Bird
类的方法,而且可以做到双重适配,也就是说在原先用到Bird
类的地方也可以使用BirdClassAdapter
,这点是对象适配器做不到的。但对象适配器可以适配Bird
的所有子类,即在初始化BirdObjectAdapter
时可以传入Bird
及其子类。
桥接模式(Bridge)
桥接模式的目的是为了将抽象部分与实现部分分离,使它们可以独立变化,以适应系统的不断发展。所以与适配器模式不同,桥接模式一般是在系统设计之初就开始使用以应对未来的变化,而不是在一个已经存在很久的旧系统中做一些修修补补的适配工作。
桥接模式的形式其实也很简单,就是利用对象组合分离接口和实现,用继承来分别扩充接口和实现:
// 抽象(暴露给客户使用的接口)
class Abstraction {
var imp: Implementor
init(imp: Implementor) {
self.imp = imp
}
func operation() {
imp.operationImp()
}
}
// 对接口的扩展
class RefinedAbstraction: Abstraction {
func otherOperation() {}
}
// 实现
protocol Implementor {
func operationImp()
}
// 对实现的扩展
class ConcreteImplementor: Implementor {
func operationImp() {
print("Do something.")
}
}
Abstraction
和Implementor
之间就是所谓的桥接关系,两者可以独立变化,并隐藏大量实现细节。
组合(Composite)
Composite 模式(翻译成组合模式总感觉怪怪的)将对象组合成树形结构以表示“部分-整体”的层次结构,使单个对象和组合对象对外表现出一致的接口。
上图是 Composite 模式的典型结构图,Composite
表示组合对象,Leaf
表示单个对象,都继承自Component
(图中未出现)。Component
是对外提供的接口,它一般包含Add(Component)
、Remove(Component)
、GetChild(int)
以及通用的Operation()
等方法。看到这里是不是感觉有点眼熟?其实 iOS 开发者整天都在跟 Composite 模式打交道,因为 UIKit 就是基于 Composite 模式构建的。UIView 就是 Component,UIView 的所有子类,比如 UIImage、UILabel 等等,都既可以作为 Composite 又可以作为 Leaf。