你的位置:首页 > ASP.net教程

[ASP.net教程]设计模式之桥接模式(Bridge)


 注:本文不属于原创,而是根据原文重新整理,原文是:我给媳妇解释设计模式:第一部分


  

      设计模式不是基于理论发明的。相反,总是先有问题场景,再基于需求和情景不断演化设计方案,最后把一些方案标准化成“模式”。所以,我们讨论每一个设计模式时,要尽量用生活中的真实问题来理解和分析。然后尝试一步步地阐述设计,并以一个能匹配某些模式的设计收尾。

设计问题与解决方案

      先让我们考虑一下下面的情况:
      我们的家里都有家用电器(比如电灯和风扇),他们都是由开关控制。 任何时候,你都可以在不改变其他东西的情况下做一些事。你可以在不更换开关的情况下换掉灯泡,也可以在不接触灯泡或者风扇的情况下更换开关,甚至可以在不接触开关的情况下,把灯泡和风扇的开关互换。

   

  灯泡和风扇

      

  不同类型的开关


      当不同的事物联系到一起时,他们应该在一个可以变更或者可以替换的系统中以便不相互影响,或者影响尽可能的小。这样让你更为方便、成本最小地去管理你的系统。可以想象,如果你要换一个你房间里的灯泡得要求你把开关也换了,你会考虑在你房子里使用这样的一个系统吗? 当然不会。 现在,让我们想一下电灯或者电风扇是怎样和开关联系起来以便更换其中一个而不会影响到其他的。当然是电线啦。 是电线以及其他的电工手段把电灯/电风扇与开关连接起来。我们可以把这概括为沟通不同系统的桥梁。基本思想是,一个事物不能直接连接另一个事物。当然,他们能够通过一些桥梁或接口连接起来。在软件世界里,我们称之为“松耦合”。
      现在,我们来尝试理解一些类似电灯/电风扇与开关类似的关键问题,同时尝试理解是怎样设计和关联它们的。
      在我们的列子里,有一些开关,这些类似普通的开关、有不同的花式开关可能有不同的种类,但是,一般情况下,他们就是开关。同时,每个开关都能开和关。
      这样的话,我们就会得到如下的Switch基类:

 1 public class Switch 2 { 3   public void On() 4   { 5    // 开关有一个“开”的按钮 6   }  7   public void Off() 8   { 9     // 开关有一个“关”的按钮10   }11 }

      同时,我们可能也需要一些特定类型的开关,譬如正常的开关、不同花式的开关等等。同样的我们扩展Switch类来实现FancySwitch和NormalSwitch:

1 public class NormalSwitch : Switch2 {3 }4 5 public class FancySwitch : Switch6 {7 }

      这两个特定的开关类可能用于它们自己特有的行为和特征,但是到目前为止,我们还是保持它们现在的简单形式。

      现在,如何处理风扇和灯呢?
      按照面向对象设计原则中的封闭原则,我认为我们需要试着在任何可能的地方做抽象处理。
      电扇和电灯情况有点不一样,它们两个不是同一种东西。对于不同的开关,我们可以用同一个基本的Switch类,但对于电扇和电灯就不大合适了,感觉用接口会更合适一点。因为,从大体上讲,它们都算是电器,那么我们可以就定义一个接口: IElectricalEquipment,用它来抽象电扇和电灯。
      那么,所有电器都有一些共性,可以被打开和关闭。那么这个接口就可以是:

1 public interface IElectricalEquipment2 {3   void PowerOn(); // 每个电器都能够被打开4   void PowerOff(); // 每个电器都能够被关闭5 }

      好了,现在我们还缺一座桥。在现实世界里,桥是电线。在对象的世界里,开关知道怎么开关电器,电器需要用某种方式跟开关连起来。可这里没有电线,我们唯一有的,是封装。

      开关并不知道电扇和电灯的存在。它只知道它可以打开或关闭某个电器IElectricalEquipment。那么,也就是说每个Switch应该拥有一个IElectricalEquipment实例。
      这里,被封装的实例,也就是IElectricalEquipment,就是这座桥。好,我们来修改一下Switch类,让它把电器封装进去:

 1 public class Switch 2 { 3   public IElectricalEquipment equipment 4   { 5     get; 6     set; 7   } 8   public void On() 9   {10     // 开关有一个打开的按钮11   }12   public void Off()13   {14     // 开关有一个关闭的按钮15   }16 }

      接下来我再定义真正的电器吧。电扇和电灯,总体上都是电器,所以它们应该实现IElectricalEquipment接口。

      电扇类:

 1 public class Fan : IElectricalEquipment 2 { 3   public void PowerOn() 4   { 5      Console.WriteLine("Fan is on"); 6   } 7   public void PowerOff() 8   { 9     Console.WriteLine("Fan is off");10   }11 }

      电灯类:

 1 public class Light : IElectricalEquipment 2 { 3   public void PowerOn() 4   { 5     Console.WriteLine("Light is on"); 6   } 7   public void PowerOff() 8   { 9     Console.WriteLine("Light is off");10   }11 }

      很好。现在该是接上开关的时候了。开关在打开和关闭的时候,必须能打开和关闭它所连接的电器。

      也就是说:
      • 当按下开关的打开按钮时,必须打开连接的电器。
      • 当按下开关的关闭按钮时,必须关闭连接的电器。
      我们想要的功能基本上是这个样子:

 

 1 static void Main(string[] args) 2 { 3   //我们有一些电器,比如风扇、电灯等,因此我们首先要创建它们 4   IElectricalEquipment fan = new Fan(); 5   IElectricalEquipment light = new Light(); 6   //我们也有一些开关,同样需要创建它们 7   Switch fancySwitch = new FancySwitch(); 8   Switch normalSwitch = new NormalSwitch(); 9 10   //让我们将电扇和电扇开关连接起来11   fancySwitch.equipment = fan;12   13   //现在开关对应了一个设备(电扇),因此可以开关设备。14   //下面这个操作将打开电扇15   //当然,在开关的这个 On() 方法里面我们必须打开电器。16   fancySwitch.On(); 17   //下面这个操作将关闭电扇18   fancySwitch.Off();19 20   //现在我们将电灯与风扇的开关连接起来21   fancySwitch.equipment = light;22   fancySwitch.On(); // 现在它将打开电灯23   fancySwitch.Off(); // 现在它将关闭电灯24 }

 

      那么,开关的On()方法应该调用电器的TurnOn()方法,而它的Off()方法应该调用电器的TurnOff()方法,Switch类应该是这个样子:

 1 public class Switch 2 { 3   public void On() 4   { 5     Console.WriteLine("Switch on the equipment"); 6     equipment.PowerOn(); 7   } 8   public void Off() 9   {10     Console.WriteLine("Switch off the equipment");11     equipment.PowerOff();12   }13 }

      这个电扇显示是可以换开关的。而且,反过来也是可以换的,可以不修改电扇和电灯,直接更换开关,例如,我们可以把电灯的开关从FancySwitch换成NormalSwitch:

1 normalSwitch.equipment = light;2 normalSwitch.On(); //It should turn on the Light now3 normalSwitch.Off(); //It should be turn off the Light now

      看到没,我们可以在不影响任何一方的情况下,改变另一方。这个设计看起来很不错,而且相当的优雅。其实四人帮(GoF)管这个设计叫桥接模式。

      一般来说,两个系统不应该直接地互相联接和依赖。相反,他们应该通过抽象来联接或依赖(参见依赖倒置和开闭原则),这样它们就是松耦合的,我们就可以在必要时轻松地修改实现,而不对系统的其它部分造成太大影响。

桥接模式的定义

      我们来看一下桥接模式的定义吧:

 

    “把抽象和实现解耦,使得它们可以独立地变化”

      桥接模式的类图结构如下:

            
      在我们的例子里,Abstraction是基础的Switch类,RefinedAbstraction是某个具体的开关类(FancySwitch和NormalSwitch),Implementor是IElectricalEquipment接口,ConcreteImplementorA和ConcreteImplementorB分别是Fan和Light类。
      桥接模式是所有面向对象设计模式的基础。因为:
      • 它能教你如何抽象地思维,这可是OO设计模式的关键。
      • 它实现了基本的OOD原则。
      • 它很好理解。
      • 如果能正确地理解它,学习其它模式就易如反掌了。