桥接模式
在面向对象的编程中,我们经常会使用继承,但事实上,很多情况用继承会带来麻烦,因为对象的继承关系是在编译时就定义好了,所以无法再运行时改变从父类继承的实现,子类的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。
因此,我们应该尽量使用合成/聚合,尽量不要使用类的继承。聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。有限使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并被不太可能增长为不可控制的庞然大物。
在众多设计模式中,桥接模式是对合成/聚合的典型应用,假如实现系统可能有多个角度分类,每一种分类都有可能发生变化,那么就把这多种角度分离出来,让他们独立变化,减少它们之间的耦合。
例如,手机既有多个品牌,又有多种软件,就可以使用桥接模式实现,首先实现多种软件类:
abstract class HandsetSoft { public abstract void Run(); } class HandsetGame : HandsetSoft { public override void Run() { Console.WriteLine("Run Handset Game."); } } class HandsetAddressList : HandsetSoft { public override void Run() { Console.WriteLine("Run Handset Address List."); } }
下面实现多个手机品牌,在品牌类中使用聚合,引入不同的手机软件类:
abstract class HandsetBrand { protected HandsetSoft soft; //设置当前需要使用的软件 public void SetHandsetSoft(HandsetSoft soft) { this.soft = soft; } abstract public void Run(); } class HandsetABrand : HandsetBrand { public override void Run() { Console.WriteLine("HandsetA Run:"); soft.Run(); } } class HandsetBBrand : HandsetBrand { public override void Run() { Console.WriteLine("HandsetB Run:"); soft.Run(); } }
客户端调用代码如下:
HandsetABrand brandA = new HandsetABrand(); brandA.SetHandsetSoft(new HandsetGame()); brandA.Run(); HandsetBBrand brandB = new HandsetBBrand(); brandB.SetHandsetSoft(new HandsetAddressList()); brandB.Run();
运行结果如下:
HandsetA Run:
Run Handset Game.
HandsetB Run:
Run Handset Address List.
在仅使用继承时,结构图如下,如果需要增加一个软件或一个手机品牌,都至少需要增加两个类,桥接模式巧妙地使用聚合关系替代了类的继承,这样,无论增加手机软件或是手机品牌,都只需要增加一个类既可。