引入
设想如果要绘制矩形,圆形,正方形,我们至少需要3个类型类,但是如果绘制的图形需要具有不同的颜色,如红色,绿色,蓝色等,此时至少有如下两种设计方案:
-
第一张设计方案是为每一种形状都提供一套各种颜色的版本
1475571-20190112180432090-1967755010.png
-
第二种设计方案是根据实际需要对形状和颜色进行组合
1475571-20190112180443952-1070829097.png
对于有两个变化维度(即两个变化的原因)的系统,采用方案二来进行设计系统中类的个数更少,且系统扩展更为方便,设计方案二即为桥接模式的应用。桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量。
模式定义
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
通俗点讲就是在不同的地方之间搭一座桥,让他们连接起来,可以相互通讯和使用。
在模式中,就是为被分离了的抽象部分和实现部分来搭桥。
桥接模式中的桥接是单向的,也就是只能是抽象部分的对象去使用实现部分的对象,而不能反过来,也就是个单向桥。
抽象化角色就像是一个水杯的手柄,而实现化角色和具体实现化角色就像是水杯的杯身,这就是此模式别名“柄体”的来源。
涉及角色
抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同。实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现
桥接模式分析
桥接模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”。这句话有三个关键词,也就是抽象化,实现化和脱耦。
- 抽象化
存在于多个实体中的共同的概念性联系,就是抽象化。作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待。 - 实现化
抽象化给出的具体实现,就是实现化。 - 脱耦
所谓耦合,就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱,或称脱耦。在这里脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改成弱关联。
将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改成弱关联。因此,桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对独立的变化。这就是桥接模式的用意
桥接模式的优点
- 分离抽象接口及其实现部分
- 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
- 实现细节对客户透明,可以对用户隐藏实现细节。
桥接模式的缺点
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
桥接模式的应用场景
-
Java 语言通过Java 虚拟机实现了平台的无关性
1303036-20190816160837473-339019914.png
-
JDBC 驱动器
桥接模式在Java应用中一个非常典型的例子就是JDBC驱动器。JDBC为所有的关系型数据库提供一个通用的界面。一个应用系统动态的选择一个合适的驱动器,然后通过驱动器向数据库引擎发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。
抽象角色可以针对任何数据库引擎发出的查询指令,因为抽象角色并不直接与数据库引擎打交道,JDBC驱动器负责这个底层的工作。由于JDBC驱动器的存在,应用系统可以不依赖数据库引擎的细节而独立的演化;同时数据库引擎也可以独立于应用系统的细节而独立的演化。两个独立的等级结构图如下图所示,左边是JDBC API 的等级结构,右边是JDBC驱动器的等级结构,应用程序是建立在JDBC API 的基础之上的。
5408072-42a17c6f67ddf43e.png
JDBC 等级结构图
应用系统作为一个等级结构,与JDBC驱动器这个等级结构是相对独立的,它们之间没有静态的强关联。应用系统通过委派与JDBC驱动器相互作用,这是一个桥接模式的例子。
JDBC 这种架构,把抽象部分和具体实现部分分离开来,从而使得抽象部分和具体实现部分都可以独立的扩展。对于应用程序而言,只要选用不同的驱动,就可以让程序操作不同的数据库,而无需更改应用程序,从而实现在不同的数据库上移植;对于驱动程序而言,为数据库实现不同的驱动程序,并不会影响应用程序。
适合使用桥接模式的情况
- 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
- 抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化值子类的对象和一个实现化子类的对象进行组合,即系统需要抽象化角色和实现化角色进行动态耦合
- 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
- 虽然在系统中使用继承时没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
- 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
类结构
# 接口实现类
class Implementor:
def Operation(self):
raise NotImplementedError
class ConcreteImplementorA(Implementor):
def Operation(self):
print("实现 A 方法")
class ConcreteImplementorB(Implementor):
def Operation(self):
print("实现 B 方法")
# 抽象类
class Asbtraction():
def __init__(self, implementor):
self.implementor = implementor
def Operation(self):
raise NotImplementedError
class RefineAbstraction(Abstraction):
def Operation(self):
self.implementor.Operation()
if __name__ == "__main__":
a = ConcreteImplementorA()
b = ConcreteImplementorB()
aa = RefineAbstraction(a)
aa.Operation()
bb = RefineAbstraction(b)
bb.Operation()
示例代码
# 抽象类:人
class people:
def set_skill(self, skill):
self.skill = skill
def perform_skill(self):
pass
# 具体抽象类: 花匠
class hua_j(people):
def perform_skill(self):
print("我是花匠")
self.skill.perform_skill()
#具体抽象类:木匠
class mu_j(people):
def perform_skill(self):
print('我是木匠')
self.skill.perform_skill()
#具体抽象类:铁匠
class tie_j(people):
def perform_skill(self):
print('我是铁匠')
self.skill.perform_skill()
#功能类,也是实现类
class skill:
def perform_skill(self):
pass
#具体功能类,也是具体实现类 种花
class skill_hua(skill):
def perform_skill(self):
print('我会种花')
#具体功能类,也是具体实现类 做木桌子
class skill_mu:
def perform_skill(self):
print('我会做木桌子')
#具体功能类,也是具体实现类 做铁桌子
class skill_tie:
def perform_skill(self):
print('我会做铁桌子')
#具体功能类,也是具体实现类 做老师
class skill_teacher:
def perform_skill(self):
print('我会做老师,可以教学生')
#具体功能类,也是具体实现类 做家具
class skill_jj:
def perform_skill(self):
print('我会做家具')
def main():
h=hua_j() #花匠
m=mu_j() #木匠
t=tie_j() #铁匠
sh=skill_hua()# 本事:会种花
sm=skill_mu() #本事:会做木头桌子
st=skill_tie() #本事:会做铁桌子
s_t=skill_teacher() #本事:会教学生
s_jj=skill_jj() #本事:会做家具
h.set_skill(sh) #给花匠set种花的本事
h.perform_skill() #花匠 种花
h.set_skill(s_t) #给花匠set做老师的本事
h.perform_skill() #花匠教学生
print('=============')
m.set_skill(sm) #给木匠set 做木桌子的本事
m.perform_skill() #木匠 做木桌子
m.set_skill(s_t) #给木匠set做老师的本事
m.perform_skill() #木匠教学生
m.set_skill(s_jj) #给木匠set做家具的本事
m.perform_skill() #木匠做家具
print('=============')
t.set_skill(st) #给木匠set 做木桌子的本事
t.perform_skill() #木匠 做木桌子
t.set_skill(s_t) #给木匠set做老师的本事
t.perform_skill() #木匠教学生
t.set_skill(s_jj) #给木匠set做家具的本事
t.perform_skill() #木匠做家具
if __name__ == '__main__':
main()
网友评论