`

【转】设计模式之Bridge(桥接)

 
阅读更多

注释:以下资料都是个人从网上收集起来的,会注明来源地址

 

如果你想要写一个游戏,并且想让这个游戏同时支持PC和手机,那么怎么样的设计可以避免写两套代码,并且不影响可扩展性呢?说起来还是比较简单的,只要把对平台的依赖部分抽取成抽象的接口(比如说绘图部分),并且针对抽取出来的接口,分别实现PC版和手机版就行了。系统的其他部分只要调用那套抽取出来的接口就可以完成所有的功能。这样来看,系统的其他部分是不依赖具体的平台的,也就具有了良好的扩展性。这个就是Bridge模式的应用。

       1、定义 
       将抽象和实现相分离,使二者可以独立的变化。(GOF)

       2、结构图 
 
                                           图一:Bridge模式的一个结构图

 

        3、例子 
        任何设计模式都是应用于不同的需求的,这里我们构造了一个有关汽车的需求来应用Bridge模式。(不过,由于个人对车了解不多,所以有可能例子不是很恰当,不要纠结于这一点哦)

         ★需求 
          1. 概括的讲,我们的需求就是实现各种各样的汽车。从细节上讲,我们知道,汽车
以用途分类,有卡车,巴士,小汽车等类别;
以动力系统分类,有汽油型,柴油型,电力型,天然气型,生物能,油电混合型,太阳能型等等。也就是说,有汽油型的卡车,巴士,小汽车,也有柴油型的卡车,巴士,小汽车,当然还有其他各种类型的卡车,巴士,小汽车。这里为了方便,我们对需求做一个简化,只考虑卡车、巴士,汽油型和柴油型。也就是说,两两组合之后,就会有四种车型:汽油型卡车,柴油型卡车,汽油型巴士和柴油型巴士。

         2. 任何车都有开始和停止的功能。

         3. 卡车有载货的功能,巴士有载客的功能。

         ★初次设计 
根据以上的需求描述,想必所有人都可以设计出如图二所示的类结构来。
 
                   图二:关于汽车的最初的设计图

         ★重构 
        虽然图二所示的图任何人都可以设计出来,但估计没有人是满意这样的设计的。这样的设计,不但重复代码多,而且要扩展或有所修改的话,所有的类都得大动干戈。
这里我们就对其做一次重构。
从需求3我们知道,卡车和巴士分别有专有的功能(载货,载客),并且这个功能是和动力系统无关的,而开车和停止的功能都是与动力系统有关的。所以我们这里先把卡车和巴士区分出来,并且分别实现载货和载客的功能。那么就是如图三所示的结构了。
 
                         图三:一次重构后的设计图

       ★再重构 
       经过图三的重构之后,结构就清晰了些,并且代码的重复度也减少了一部分(载货,载客)。但是我们还是可以看出不足之处,较明显的地方就是,同样的动力系统,它的开始和停止肯定是基本相似的。

其实这里是有一个隐藏需求的。
       1. 对于同一种类型的车来说,无论采用的动力系统是哪一种,其驾驶方法是相同的。也就是说,会开汽油型卡车的司机开起柴油型的卡车来也是驾轻就熟的。否则,有多少种车就得有多少种驾驶方法的话,司机没事就得考驾照了,人手n本驾照,很恐怖的。
       2. 同样,对于同一种动力系统来说,无论安装在哪一种车型上,恐怕主要是动力的大小是不同的,启动和停止的方法(在软件系统中来说应该是接口)应该是相同的。

       下面我们根据以上的隐藏需求对这个设计进行再重构。既然所有的车都有动力系统,而且每个动力系统的接口都可以是一样的,那我们就把这个动力系统提取出来作为一个单独的类。这也是应用了Bridge模式之后的结果。具体结构图参见图四。

 
                                   图四:应用了Bridge模式之后的重构结果

       JDBC的例子 
网络上看到有朋友对JDBC到底是不是应用了Bridge模式有一些分歧。这一点我们还是很明确的,JDBC确实是应用了Bridge模式。
JDBC其实只是Sun定义出来的一套规范,java.sql下的真正内容大部分都是接口。而其他的数据库厂商则可以分别提供自己的jdbc实现来支持自家的数据库。作为应用开发者,完全可以只靠Sun的这套JDBC接口来完成所有的应用开发,而无需关心数据库是哪一家的。作为数据库厂商,只需要提供自家数据库的实现就可以了,完全不需要考虑自家的数据库会被用来做什么样的应用。这也就是抽象(数据库的应用)和实现(各厂商的JDBC实现)的分离。
再说到代码,想想我们用JDBC写数据库应用时,与具体数据库关联的代码只有一行。

[java] view plaincopy
  1. Class.forName("com.mysql.jdbc.Driver"); // driver name  


       这行代码所做的事情也非常简单,就是创建一个自己(com.mysql.jdbc.Driver)的实例,注册到java.sql.DriverManager中。

[java] view plaincopy
  1. package com.mysql.jdbc;  
  2. import java.sql.DriverManager;  
  3. import java.sql.SQLException;  
  4. public class Driver extends NonRegisteringDriver  
  5.     implements java.sql.Driver  
  6. {  
  7.     static  
  8.     {  
  9.         try  
  10.         {  
  11.             DriverManager.registerDriver(new Driver());  
  12.         } catch (SQLException E) {  
  13.             throw new RuntimeException("Can't register driver!");  
  14.         }  
  15.     }  
  16. }  



之后在调用DriverManager.getConnection()时就会从已注册的driver中寻找合适的项并返回。

简单的一个结构图如图五。
 
                                                     图五:JDBC中Bridge模式的应用

在这里,有一点小小的意外,DriverManager提供了一组静态方法,并且私有化了自己的构造函数。可以简单的认为只有一个实例存在(实际上数据是以类变量的形式存在)。这个看似意外的设计,导致DriverManager不再具备扩展性,但其实是一种更合适的设计,因为我们也不需要n多的DriverManager的实例存在,也不需要它还有任何扩展的可能。
JDBC的Bridge模式其实可以算是Bridge模式的另外一种表现。我们在应用设计时,也要考虑具体的场景和需求,来选用合适的结构,而不能一味的套用。
另外需要说明一下的是,图五只是用了两个类来表示Bridge模式的应用。但在JDBC中,并不仅仅是Driver被抽象出来了,还有Connection,Statement等一组Interface都是被抽象出来的实现。

      适用于 
       ★类的某部分功能会剧烈变化时,把抽象和实现分离,则抽象的部分在开发时不会受到实现部分变动的影响
       ★想要使抽象和实现部分分别开发,互不依赖时
       ★想要避免抽象和实现的强耦合,使实现部分可以在运行时被动态的切换
       ★想要对抽象隐藏实现部分或者对实现隐藏抽象部分时(外包时可用)

     心得总结 
Bridge模式所应用的核心设计思想是针对接口编程,组合优于继承。
从上面的汽车的设计重构过程来看,我们也可以得出这样的结论,如果在抽取类的时候就严格按照CRC的原则进行的话,那么动力系统是很容易被抽取成一个单独的类,并且作为汽车的一个组成部分而存在。当然,Car和Engine之间只能是聚合关系,而不是组合关系,因为Engine完全可以独立存在,或者安装到其他的机械设备上。

参考资料 
1. 软件设计 Bridge模式(新的理解,新的参考)
   http://humingke1984.blog.163.com/blog/static/34777159201062444653948/
2. 从桥接模式与策略模式谈起
   http://www.blogjava.net/wangle/archive/2007/04/25/113545.html
3. Bridge模式学习笔记
   http://blog.csdn.net/zjibo/archive/2009/09/10/4540030.aspx
4. 再论桥接模式(上)纸上谈兵
   http://blog.csdn.net/jyk/archive/2009/12/04/4936430.aspx
5. Bridge模式,Decorator模式(转载)
   http://www.360doc.com/content/08/0801/15/63912_1497477.shtml

来源:http://blog.csdn.net/superbeck/article/details/5969884

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics