舞步者

带她一起去周游世界
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

设计模式之七Bridge(桥接)模式

Posted on 2008-06-02 11:25  Kevin_Zhang  阅读(150)  评论(0)    收藏  举报
我们以日志记录为例,来说明此模式的应用,先看下面的基本代码:
 public abstract class Log
    
{
        
public abstract void Write(string msg);
    }


public class DataBaseLog:Log
    
{
        
public override void Write(string msg)
        
{
            
        }

    }


 
public  class FileLog:Log
    
{
        
public override void Write(string msg)
        
{
            
        }

    }
从代码,我们知道,有两种记录日志的方式:一种是数据库相关的,一种是跟文件相关的,我们进行了抽象,然后用两个字类来实现抽象;现在有这么一种情况:我们
是并没有考虑到平台的变化,默认认为是在同一平台上实现的;如果需求在平台上是有变化的,比如在.Net平台,JAVA平台,甚至可能是Borland平台,我们可能再继续继承下去,例如这样的代码:
public class NetDataBaseLog:DataBaseLog
    
{
        
public override void Write(string msg)
        
{
            
//.Net平台的数据库日志记录实现
        }

    }

 
public class JavaDataBaseLog:DataBaseLog
    
{
        
public override void Write(string msg)
        
{
           
//JAVA平台的数据库日志记录实现

        }

    }

 
public class OtherDataBaseLog:DataBaseLog
    
{
        
public override void Write(string msg)
        
{
            
//其他某一平台的数据库日志记录实现
        }

    }
上面的代码我们只实现了数据库相关的不同平台的日志记录实现,文件相关的实现方式省略了;如果我们再引入一种新的平台,按这种思路,我们应该新增类,提示新平台下的两种记录方式的实现;如果我们在引入一种新的日志记录方式,我们同样要新增类,提供不同平台的实现;这些我们提供的这些类的实现中,必然存在重复的代码,如果变化在平台和实现两个两个方向上同时存在的话,必然导致类的迅速膨胀,导致类的层次过于复杂,这必然带来代码维护的成本。
我们说一个类应该只有一个引起它变化的因素,但是上面的代码显然违背这一单一职责原则,Log类现在的变化因素有两个,出于这个问题,我们需要使用桥接模式。
先看结构图:

我们先不必考虑这个模式具体的实现,先自己再来分析一下上面所碰到的问题如何处理。
我们现在可以肯定的是LOG这个抽象类应该可以定下来,将来的具体的实现只要继承实现就可以,这是一个方向的变化;我们也知道将来会发生平台上的变化;我们上面的代码是使用了继承的方式解决了变化的问题,但却带来了新的问题,继承使这两种变化的实现绞在了一起;既然平台会像实现方式那样激烈变化,那么我们当然可以像实现方式那样有如下的代码:
 public abstract class ImpLog
    
{
        
public abstract void Execute(string msg);
    }

public class NetImpLog:ImpLog
    
{
        
public override void Execute(string msg)
        
{
            
        }

    }

 
public class JavaImpLog:ImpLog
    
{
        
public override void Execute(string msg)
        
{
            
        }

    }

public class OtherImpLog:ImpLog
    
{
        
public override void Execute(string msg)
        
{
            
        }

    }

我们可以发现:记录方式的变化和平台的方向上的变化是一种平行关系,但是现在的问题是如何将这两者联系起来?我们可以使用对象组合的方式来解决:在我们的Log类里面,放一个ImpLog的字段,那么Log及其子类修改后如下:
 public abstract class Log
    
{
        
protected ImpLog implementor;
        
public Log(ImpLog imptor)
        
{
            
this.implementor = imptor;
        }

        
public ImpLog Implementor
        
{
            
get
            
{
                
return this.implementor;
            }

            
set
            
{
                
this.implementor = value;
            }

        }

        
public abstract void Write(string msg);
    }

 
public class DataBaseLog:Log
    
{
        
public DataBaseLog(ImpLog imp)
            : 
base(imp)
        

            
//.
        }

        
public override void Write(string msg)
        
{
            
//
            implementor.Execute();
            
//
        }

    }

 
public  class FileLog:Log
    
{
        
public FileLog(ImpLog imp)
            : 
base(imp)
        

            
//.
        }

        
public override void Write(string msg)
        
{
            
//
            implementor.Execute();
            
//
        }

    }

 
public class OtherDataBaseLog:DataBaseLog
    
{
        
public OtherDataBaseLog(ImpLog imp)
            : 
base(imp)
        

        
        }

        
public override void Write(string msg)
        
{
            
//其他某一平台的数据库日志记录实现

            
//
            implementor.Execute();
            
//
        }

    }
我们通过对象组合的方式,在LOG类里面放置了平台基类的一个字段,在构造函数里面初始化它,并添加了它的属性,且可以让LOG子类继承到这个字段;这个其实就是我们的桥模式,通过对象的组合方式,将两个方向上的变化桥接在一起;下面是我们的客户调用代码:
 static void Main(string[] args)
        
{
            ImpLog impNet 
= new NetImpLog();
            DataBaseLog logDb 
= new DataBaseLog(impNet);
            logDb.Write(
"Net Database Log");

            FileLog logFi 
= new FileLog(impNet);
            logFi.Write(
"Net File Log");
            logFi.Write();

            ImpLog impJava 
= new JavaImpLog();
            DataBaseLog logDb2 
= new DataBaseLog(impJava);
            logDb2.Write(
"Java Database Log");

            FileLog logFi2 
= new FileLog(impJava);
            logFi2.Write(
"Java File Log");
        }
可以看出,我们组合出了很多情况的日志记录实现,而不要添加那么多的类,这就是桥接模式所带来的便捷。
桥模式一个核心就是”多维度的非常强的变化“。如果其中一个变化并是剧烈,或者说两个变化并不会产生纵横交错的效果,那么并不适合使用这个模式。
桥模式类似于多继承,比如我们放置的平台基类的字段,这是不是有一种继承了平台类的味道?但是比真正的多继承有更多的优点,因为多继承往往带来的是违背单一职责的原则。