设计模式之Birdge(桥接)模式
1、出现原因
1、同一个类型,有两个变化的维度(两个维度的抽象:一个抽象部分的抽象,一个实现部分的抽象)
2、如何应对这种“多维度的变化”?如何利用面向对象技术来使得同一类型可以轻松地沿着两个方向变化,而不引入额外的复杂度?
2、具体的例子
数据库操作
操作类型:增删改查 (具体功能的实现部分)
操作对象:客户,订单,产品…(对应上面的增删改查是 1对多的关系(通过聚合关系联系起来))
手机软件
软件功能:通讯录,游戏
支持品牌:M(摩托骡拉),N(喏鸡呀)
计算机软件
软件功能:游戏,开发工具,绘图软件…
运行平台:Windows,Unix….
过度的使用继承:
——以手机软件为例:
以手机品牌分类:
以手机软件分类:
我们仔细观察,如果给上面的增加一个手机软件,,那么每个手机品牌都要增加这个手机软件,都要变化,耦合度太高了。
使用桥接模式重构上面的:
3、意图:
将抽象部分与实现部分分离,使它们都可以独立地变化。
4、结构图:
5、代码演示
1 //桥接模式:两个维度的变化(一个是抽象,一个是实现),并且 这两个维度 是一对多的(抽象 1对多 实现)/聚合 关系;通过桥接模式将 两个维度(抽象和实现) 隔离。将 两个维度的变化 封装起来,不管一个维度 怎么 变化 都不会影响另一个维度的 变化。。所以 耦合度 降低了 很多(相比以前的 继承关系) 2 3 4 //第一个维度(实现部分) 5 public interface IPhoneSoftware 6 { 7 void Start(); 8 } 9 10 11 12 //第二个维度(抽象部分) 13 14 public abstract class PhoneBrand 15 { 16 private List<IPhoneSoftware> phoneSoftwares = new List<IPhoneSoftware>(); 17 18 public void Instance(IPhoneSoftware software) 19 { 20 phoneSoftwares.Add(software); 21 } 22 23 24 public void StareAllSoftware() 25 { 26 if (phoneSoftwares.Count <= 0) 27 { 28 throw new Exception("没有可以执行的手机软件!"); 29 } 30 else 31 { 32 foreach (var item in phoneSoftwares) 33 { 34 item.Start(); 35 } 36 } 37 } 38 } 39 40 41 //第一个维度(实现部分:实现的功能) 42 public class KuGou : IPhoneSoftware 43 { 44 public void Start() 45 { 46 Console.WriteLine("酷狗音乐已经开启,可以欣赏音乐了!"); 47 } 48 } 49 50 public class QQ : IPhoneSoftware 51 { 52 public void Start() 53 { 54 Console.WriteLine(" QQ已经开启了,可以进行聊天了!"); 55 } 56 } 57 58 59 //第二个维度(抽象部分的具体的) 60 public class Samsung : PhoneBrand 61 { 62 63 } 64 65 public class Iphone : PhoneBrand 66 { 67 }
客户端代码:
1 //创建抽象 2 PhoneBrand samsung = new Samsung(); 3 //创建实现 4 IPhoneSoftware soft1 = new QQ(); 5 IPhoneSoftware soft2 = new KuGou(); 6 //将抽象和实现相结合 7 samsung.Instance(soft1); 8 samsung.Instance(soft2); 9 10 samsung.StareAllSoftware();
6、实现要点
1、Abstraction(抽象部分):定义抽象类的接口;;并维护指向Implementor类(实现部分)的对象指针。(抽象部分变化不会影响到实现部分,实现部分变化不会影响到抽象部分,因为他们是通过 聚合关系 连接起来的,扩展只是对他们各自的抽象 进行扩展变化)
2、Implementor:定义实现类的接口,该接口不一定要与Abstraction的接口完全一致。事实上这两个接口可以完全不同。(因为是对 各自方面的抽象(是两个不同的维度),通过 聚合关系 联系起来)
3、一般而言,Implementor接口仅提供基本操作(实现部分的具体功能),而Abstraction(是对 具体实现的 调用)则定义了基于操作的较高层次的操作。
7、效果
1、将接口(抽象)与实现分离:一个接口可以有若干实现(聚合关系),一个实现也可以为若干对象服务(一个实现的部分的具体功能 可以进行 复用,,可以多个对象(抽象下面具体的 扩展的抽象)里面进行使用),表示逻辑的对象可以动态地与实现功能的对象组合。
2、提高可扩充性:逻辑和实现都可以通过类层次的扩展进行扩充。
3、逻辑和实现被封装在不同的对象中,逻辑对实现的引用是动态进行的。在实际中,需要分别用不同的“工厂”创建逻辑和实现,然后组装。
8、适用性
1、不希望在业务(抽象部分)和业务的软件(实现部分)实现之间存在固定的绑定关系。例如,不希望“入库”业务过程和具体的数据库访问技术或数据库管理系统有过于密切的关系。
2、希望类的抽象和实现部分可以扩充,进而实现不同的抽象接口和实现部分的组合。(实现部分可以在不同的抽象部分 可以被复用)
修改实现部分对用户不产生影响,即代码无须重新编译。(因为用户操作的是实现的接口,具体的实现接口下面的实现部分已经封装起来了)
3、复用实现部分。由于实现部分所处的层次较低,因此可以被多种业务模块复用。例如,数据库访问模块可以用在多种业务单元中。
9、总结
1、Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
2、所谓抽象和实现沿着各自维度的变化,即“子类化”它们。得到各个子类之后,便可以任意组合它们。
3、Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
4、Bridge模式的应用一般在“两个非常强的变化维度”(含有两个方向可以进行派生),有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式(设计模式是一种思想,并不一定要硬套)。