桥接模式(Bridge Pattern)

简介

桥接模式把事物对象和其具体行为、具体特征分离开来,使它们可以各自独立的变化。事物对象仅是一个抽象的概念。如“圆形”、“三角形”归于抽象的“形状”之下,而“画圆”、“画三角”归于实现行为的“画图”类之下,然后由“形状”调用“画图”。

如果有一个N品牌的手机,它有个小游戏,我要玩游戏,程序应该如何写?

  1. //N品牌的手机中的游戏
  2. class HandSetNGame
  3. {
  4. public void Run()
  5. {
  6. Console.WriteLine("运行N品牌");
  7. }
  8. }

客户端

  1. HandSetNGame game = new HandSetGame();
  2. game.run();

现在又有一个M品牌的手机,也是小游戏,客户端也可以调用,如何做?

  1. class HandSetGame
  2. {
  3. public virtual void Run()
  4. {
  5. }
  6. }

M品牌手机游戏和N品牌手机游戏

  1. class HandSetMGame : HandSetGanme
  2. {
  3. public override void Run()
  4. {
  5. Console.WriteLine("运行M品牌手机游戏");
  6. }
  7. }
  8. class HandSetNGame : HandSetGanme
  9. {
  10. public override void Run()
  11. {
  12. Console.WriteLine("运行N品牌手机游戏");
  13. }
  14. }

然后,由于手机都需要通讯录功能,于是N品牌和M品牌都增加了通讯录的增删该查功能,如何处理?

  1. //手机品牌
  2. class HandSetBrand
  3. {
  4. public virtual void run()
  5. {
  6. }
  7. }
  1. //手机品牌M
  2. class HandSetBrandM:HandsetBrand
  3. {
  4. }
  5. //手机品牌N
  6. class HandSetBrandM:HandsetBrand
  7. {
  8. }

下属的各自通讯录类和游戏类

  1. //手机品牌M的游戏
  2. class HandSetBrandMGame:HandSetBrandM
  3. {
  4. public override void Run()
  5. {
  6. Console.WriteLine("运行M品牌手机游戏");
  7. }
  8. }
  9. //手机品牌N的游戏
  10. class HandSetBrandMGame:HandSetBrandM
  11. {
  12. public override void Run()
  13. {
  14. Console.WriteLine("运行N品牌手机游戏");
  15. }
  16. }
  17. //手机品牌M的通讯录
  18. class HandSetBrandMAddressList:HandSetBrandM
  19. {
  20. public override void Run()
  21. {
  22. Console.WriteLine("运行M品牌手机通讯录");
  23. }
  24. }
  25. //手机品牌N的
  26. class HandSetBrandMAddressList:HandSetBrandM
  27. {
  28. public override void Run()
  29. {
  30. Console.WriteLine("运行N品牌手机通讯录");
  31. }
  32. }

客户端调用代码

  1. static void Main(strring[] args)
  2. {
  3. HandSetBrand ab;
  4. ab = new HandSetBrandMAddressList();
  5. ab.Run();
  6. ab = new HandSetBrandNAddressList();
  7. ab.Run();
  8. ab = new HandSetBrandMGame();
  9. ab.Run();
  10. ab = new HandSetBrandNGame();
  11. ab.Run();
  12. Console.Read();
  13. }

如果我现在需要每个品牌都增加一个MP3音乐功能,如何做?还增加子类?

对象的继承关系是在编译时就定义好的,所以无法在运行时改变从父类继承的实现,子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其它更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。


合成(Composition)/聚合(Aggregation)复用原则,尽量使用合成/聚合,尽量不要使用类继承。


聚合表示一种弱的’拥有’关系,体现的是A对象可以包含B对象,但是B对象不是A对象的一部分。合成则是一种强的’拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

比如说 大雁有翅膀,大雁和翅膀是部分和整体的关系,并且它们生命周期是一样的,于是它们就是合成关系。而大雁是群居动物,所以每只大雁都属于一个雁群,一个雁群可以有很多大雁,所以大雁和雁群是聚合关系。

优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

像’游戏’,’通讯录’,’MP3音乐’,如果我们可以让其分离与手机的耦合,那么可以大大减少面对新需求改动过大的不合理情况。

松耦合的程序

  1. //手机软件
  2. abstract class HandsetSoft
  3. {
  4. public abstract void Run();
  5. }

游戏,通讯录等具体类

  1. //手机游戏
  2. class HandsetGame : HandSetSoft
  3. {
  4. public override void Run()
  5. {
  6. Console.WriteLine("运行手机游戏");
  7. }
  8. }
  9. //手机通讯录
  10. class HandsetAddressList : HandSetSoft
  11. {
  12. public override void Run()
  13. {
  14. Console.WriteLine("运行手机通讯录");
  15. }
  16. }
  1. //手机品牌
  2. abstract class HandsetBrand
  3. {
  4. protected HandsetSoft soft;
  5. //设置手机软件
  6. public void SetHandSetSoft(HandSetSoft soft)
  7. {
  8. this.soft = soft;
  9. }
  10. //运行
  11. public abtract void run();
  12. }

品牌N品牌M具体类

  1. //手机品牌N
  2. class HandSetBrandN: HandBrand
  3. {
  4. public override void Run()
  5. {
  6. soft.Run();
  7. }
  8. }
  9. //手机品牌M
  10. class HandSetBrandM: HandBrand
  11. {
  12. public override void Run()
  13. {
  14. soft.Run();
  15. }
  16. }

客户端代码

  1. static void Mian(string[] args)
  2. {
  3. HandSetBrand ab;
  4. ab = new HandSetBrandN();
  5. ab.setHandSetSoft(new HandSetGame());
  6. ab.run();
  7. ab.setHandSetSoft(new HandSetAddressList());
  8. ab.run();
  9. ab = new HandSetBrandM();
  10. ab.setHandSetSoft(new HandSetGame());
  11. ab.run();
  12. ab.setHandSetSoft(new HandSetAddressList());
  13. ab.run();
  14. Console.Read();
  15. }

现在我们要再加个功能 比如说MP3功能,只要增加这个类就好了,不会影响其它任何类。

  1. //手机MP3播放
  2. class HandsetMP3 : HandSetSoft
  3. {
  4. public override void Run()
  5. {
  6. Console.WriteLine("运行手机MP3");
  7. }
  8. }

如果要增加S品牌,只需要增加一个品牌子类就可以了。

  1. //手机品牌S
  2. class HandSetBrand: HandSetBrand
  3. {
  4. public override void Run()
  5. {
  6. soft.Run();
  7. }
  8. }

桥接模式

桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

什么叫抽象与它的实现分离,这并不是说,让抽象与其派生类分离,因为这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象。

桥接模式 - 图1

桥接模式基本代码

桥接模式 - 图2

桥接模式把两个角色之间的继承关系改为聚合关系,从而使二者可以各自独立的变化。把原来在基类的实现化细节抽象出来,再构造到一个实现化的结构中,然后把原来的基类改造成一个抽象化的等级结构,这样就实现了系统在多个维度上的独立变化。