iOS设计模式-桥接模式
问题:
假如:我们需要大中小3种型号的画笔,能够绘制12种不同的颜色,如果使用蜡笔,需要准备3x12=36支,但如果使用毛笔的话,只需要提供3种型号的毛笔,外加12个颜料盒即可,涉及到的对象个数仅为3+ 12= 15,远小于36,却能实现与36支蜡笔同样的功能。如果增加一种新型号的画笔,并且也需要具有12种颜色,对应的蜡笔需增加12支,而毛笔只需增加一支。为什么会这样呢?通过分析我们可以得知:在蜡笔中,颜色和型号两个不同的变化维度(即两个不同的变化原因)融合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度;但在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号对另一方都没有任何影响。如果使用软件工程中的术语,我们可以认为在蜡笔中颜色和型号之间存在较强的耦合性,而毛笔很好地将二者解耦,使用起来非常灵活,扩展也更为方便。在软件开发中,我们也提供了一种设计模式来处理与画笔类似的具有多变化维度的情况,即接下来要介绍的桥接模式。
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立的变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式
类图:

角色:
-
Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个Implementor(实现类协议)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
-
RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用Implementor中定义的业务方法。
-
Implementor(实现类协议):定义实现类的接口。
-
ConcreteImplementor(具体实现类):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象提供给抽象类具体的业务操作方法。
示例代码:
Abstraction(抽象类)
@interface Abstraction : NSObject
@property (nonatomic, strong) id<ImplementorProtocol>implementor;
- (void)operation;
@end
RefinedAbstraction(扩充抽象类)
@interface RefinedAbstractionA : Abstraction
@end
@implementation RefinedAbstractionA
- (void)operation {
NSLog(@"RefinedAbstractionA operation");
[self.implementor operationImplement];
}
@end
Implementor(实现类协议)
@protocol ImplementorProtocol <NSObject>
- (void)operationImplement;
@end
ConcreteImplementor(具体实现类)
@interface ConcreteImplementorA : NSObject<ImplementorProtocol>
@end
@implementation ConcreteImplementorA
- (void)operationImplement {
NSLog(@"ConcreteImplementorA OperationImplement");
}
@end
Client(客户端)
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Abstraction *abstraction = [NSClassFromString(@"RefinedAbstractionA") new];
id<ImplementorProtocol>imp = [NSClassFromString(@"ConcreteImplementorA") new];
abstraction.implementor = imp;
[abstraction operation];
}
@end
Log:
[29297:8922342] RefinedAbstractionA operation
[29297:8922342] ConcreteImplementorA OperationImplement
拓展(添加功能)
如果需要一个新的实现只需要在增加一个新的类ConcreteImplementorB就可以
@interface ConcreteImplementorB : NSObject<ImplementorProtocol>
@end
@implementation ConcreteImplementorB
- (void)operationImplement {
NSLog(@"ConcreteImplementorB OperationImplement");
}
@end
实验一下
Class cls = NSClassFromString(@"RefinedAbstractionA");
Abstraction *abstraction = [cls new];
id<ImplementorProtocol>imp1 = [NSClassFromString(@"ConcreteImplementorB") new];
abstraction.implementor = imp1;
[abstraction operation];
Log:
[33332:8980456] RefinedAbstractionA operation
[33332:8980456] ConcreteImplementorB OperationImplement
模式讲解
在桥接模式里面,不太好理解的就是桥接的概念,什么是桥接?为何需要桥接?如何桥接?把这些问题搞清楚了,也就基本明白桥接的含义了。
1. 什么是桥接
所谓桥接,通俗点说就是在不同的东西之间搭一个桥,让他们能够连接起来,可以相互通讯和使用。那么在桥接模式中到底是给什么东西来搭桥呢?就是为被分离了的抽象部分和实现部分来搭桥。
注意:在桥接模式中的桥接是单向的,也就是只能是抽象部分的对象去使用具体实现部分的对象,而不能反过来,也就是个单向桥。
2. 为何需要桥接
为了达到让抽象部分和实现部分都可以独立变化的目的,在桥接模式中,是把抽象部分和实现部分分离开来的(笔和颜色),虽然从程序结构上是分开了,但是在抽象部分实现的时候,还是需要使用具体的实现的,怎么办呢?抽象部分如何才能调用到具体实现部分的功能呢?很简单,搭个桥不就可以了,搭个桥,让抽象部分通过这个桥就可以调用到实现部分的功能了,因此需要桥接。
3. 如何桥接
这个理解上也很简单,只要让抽象部分拥有实现部分的接口对象,这就桥接上了,在抽象部分就可以通过这个接口来调用具体实现部分的功能。也就是说,桥接在程序上就体现成了在抽象部分拥有实现部分的接口对象,维护桥接就是维护这个关系。
优缺点
优点:
1. 分离抽象和实现部分
桥接模式分离了抽象和实现部分,从而极大地提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了。
2. 更好的扩展性
由于桥接模式把抽象和实现部分分离开了,而且分别定义接口,这就使得抽象部分和实现部分可以分别独立的扩展,而不会相互影响,从而大大的提高了系统的可扩展性。可动态切换实现。
由于桥接模式把抽象和实现部分分离开了,那么在实现桥接的时候,就可以实现动态的选择和使用具体的实现,也就是说一个实现不再是固定的绑定在一个抽象接口上了,可以实现运行期间动态的切换实现。
3. 可减少子类的个数
根据前面的讲述,对于有两个变化纬度的情况,如果采用继承的实现方式,大约需要两个纬度上的可变化数量的乘积个子类;而采用桥接模式来实现的话,大约需要两个纬度上的可变化数量的和个子类。可以明显地减少子类的个数。
缺点:
1. 增加理解难度
桥接模式的使用会增加系统的理解与设计难度,由于关联关系简历的抽象层,要求开发者一开始就针对抽象出进行设计与编程。
2.识别难度
桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。