设计模式系列-桥接模式

一、上篇回顾

        上篇我们讲述了比较常用的适配器模式,并且分析了适配器的一般使用场景:

       1、我们在使用第三方的类库,或者说第三方的API的时候,我们通过适配器转换来满足现有系统的使用需求。

       2、我们的旧系统与新系统进行集成的时候,我们发现旧系统的数据无法满足新系统的需求,那么这个时候,我们可能需要适配器,完成调用需求。

       3、我们在使用不同数据库之间进行数据同步。(我这里只是分析的是通过程序来说实现的时候的情况。还有其他的很多种方式[数据库同步])。

       并且讲述了对象适配器和类适配器的区别:

       对象适配器:不是通过继承的方式,而是通过对象组合的方式来进行处理的,我们只要学过OO的设计原则的都知道,组合相比继承是推荐的方式。

       类适配器:通过继承的方式来实现,将旧系统的方法进行封装。对象适配器在进行适配器之间的转换过程中,无疑类适配器也能完成,但是依赖性会加大,并且随着适配要

求的灵活性,可能通过继承膨胀的难以控制。

        并且由于C#中不支持多继承,以至于限制我们的继承的策略。而对象组合的方式则不会有太多的限制。

       我们在上篇中还讲述了适配器之间的转配,但是没有给出一个示例代码:

       我们这里给出适配器之间相互转换的情况,有的时候,我们发现2个适配器之间也需要进行相应的转配,例如我们上面的数据库和XML文件的查询接口,例如有的时候,我

需要把XML文件的数据转换到数据库中,或者把数据库中的数据保存成XML形式,这个时候,我们就需要定义一个对象,来完成相应的适配器之间的适配。

   image

   针对上面的说明,我们这里给出代码的核心部分:

     旧系统的查询服务

     public interface IQuery
    {
        object Query();
    }

    public class DbQuery : IQuery
    {
        public object Query()
        {
            throw new NotImplementedException();
        }
    }

    public class XMLQuery : IQuery
    {
        public object Query()
        {
            throw new NotImplementedException();
        }
    }

    旧系统的持久化服务:

    public interface ISave
    {
        bool Save(object obj);
    }

    public class XMLSave : ISave
    {
        public bool Save(object obj)
        {
            throw new NotImplementedException();
        }
    }

    public class DbSave : ISave
    {
        public bool Save(object obj)
        {
            throw new NotImplementedException();
        }
    }

    新系统适配器持久化接口

    public interface IPersistence
    {
        bool Persistence(object obj);
    }

    public class XMLPersistenceAdapter : IPersistence
    {
        XMLSave save = new XMLSave();

        public bool Persistence(object obj)
        {
            return save.Save(obj);
        }
    }

    public class DbPersistenceAdapter : IPersistence
    {
        DbSave save = new DbSave();

        public bool Persistence(object obj)
        {
            return save.Save(obj);
        }
    }

   新系统查询服务适配器:

   public interface ISelect
   {
       object GetList();
   }

    public class XMLQueryAdapter : ISelect
    {
        private XMLQuery query = new XMLQuery();
        public object GetList()
        {
            return query.Query();
        }
    }

    public class DbQueryAdapter : ISelect
    {
        private DbQuery query = new DbQuery();
        public object GetList()
        {
            return query.Query();
        }
    }

     适配器之间的转配器:

    public  interface IAdapterToAdapter
    {
         object GetData();

         bool SaveData();
    }

    通用的转配器实现方案:

    public class ConvertAdapter : IAdapterToAdapter
    {
        private Dictionary<string, AdapterSetting> settings = new Dictionary<string, AdapterSetting>();
        public ConvertAdapter()
        {
            //从配置文件中读取要转换的适配器节点指点的关系,并且配置要调用的方法
            settings.Add("", new AdapterSetting());
        }

        public object GetData()
        {
            return settings[""].AdapterName.Query();
        }

        public bool SaveData()
        {
            return settings[""].ToAdapterName.Persistence(this.GetData());
        }
    }

    这样就可以完成基本的转配服务,当然我里面没有实现出完整代码,这里只是给出思路,如果您有好的想法或者建议,可以留言回复,谢谢!

二、摘要

        本文将会讲述结构性模式中的另外一个重要的模式-桥接模式,这个模式的主要特点呢,就是解决一个对象的可能变化的因素的多个方向的依赖性。有的时候一个对象,引

起这个对象发生变化的因素很多,这个时候我们就可以考虑,把依赖具体实现,提升为依赖抽象,来完成对象和变化因素之间的低耦合,提高系统的可维护性和扩展性。而且客

户可能不知道某个因素是否发生变化或者其他的可能情况。当然桥接模式也是结构性模式中一个比较难理解的模式,我们的项目中可能使用的相对来说少一些。不过当我们的一

个复杂的对象,有多个因素引起这个对象的状态发生改变的时候,我们就可以考虑使用桥接模式,将这些因素进行抽象,让这个对象依赖于这些抽象的因素,而不是具体的因

素,这样就能很好的适应变化。

         我们来看看一个可能的场景,我们现在有一个底层的ORM,我们现在只支持CUD的操作,不支持查询服务,这个时候我们为了新增查询服务,那么我们怎么做呢?按照传

统的方式,我们可能是通过继承来实现。这个时候,可能我们会如下来做,目前我们已经为这个ORM提供了SQL与ORACLE数据库的支持。

        image通过上面,我们新增查询服务和缓存服务的时候,我们就必须基于

现有的持久化服务来通过继承来扩展,扩展出一个缓存的ORM,一个查询方案的ORM,当然如果我们发现我们还需要扩展现有的服务的时候,我们又需要通过继承来完成,这是

多么恐怖的一件事情啊,这等于有一个引起变化的因素发生改变后,就需要扩展一下上级的所有类进行继承,这是个乘法运算,我想我不用多解释,大家也是知道的!,所以这

时候桥模式的出现,很好的解决这个问题了,我们来看看桥接模式的类图吧:

       桥接模式的意图就是解决这样的场景,上面将变化的因素进行抽象,然后具体的对象通过组合或者属性注入的形式来引用变化的因素,而这个依赖关系只是依赖的是因素的

抽象,这个时候,不会因为我们改变具体因素的实现,而修改用户程序中的复杂对象。这样就满足了设计模式的要求:将抽象部分与实现部分分离,使它们都可以独立的变化。

三、本文大纲

       a、上篇回顾。

       b、摘要。

       c、本文大纲。

       d、桥接模式的特点及使用场景。

       e、桥接模式的经典实现。

       f、桥接模式的其他方案。

       g、桥接模式使用总结。

       h、系列进度。

       i、下篇预告。

四、桥接模式的特点及使用场景

         4.1、桥接模式的特点

          桥接模式的主要目的是将一个对象的变化因素抽象出来,不是通过类继承的方式来满足这个因素的变化,而是通过对象组合的方式来依赖因素的抽象,这样当依赖的因素

的具体实现发生变化后,而我们的具体的引用却不用发生改变,因为我们的对象是依赖于抽象的,而不是具体的实现。

          而且,通过这样的依赖抽象,我们在多个对象共享这样的因素的时候,就成为可能,如果我们使用的是具体的因素的共享,当我们改变这个变化因素的时候,我们必须把

使用这个因素的所有的对象,都进行相应的修改,而如果所有的引用这个变化因素的对象都依赖于抽象而不是具体的依赖呢?这也为我们的共享的提供了变化性。

         4.2、桥接模式的使用场景

                    1、当一个对象有多个变化因素的时候,通过抽象这些变化因素,将依赖具体实现,修改为依赖抽象。

                    2、当某个变化因素在多个对象中共享时。我们可以抽象出这个变化因素,然后实现这些不同的变化因素。

                    3、当我们期望一个对象的多个变化因素可以动态的变化,而且不影响客户的程序的使用时。

        如何在项目中应用桥接模式,的确是个很难把握的问题,因为具体的项目中可能我们也会遇到这样的,当一个对象,可能会有多个维度的因素,引起对象变化时,我们就可

以考虑使用请桥接模式来完成设计实现。

五、桥接模式的经典实现

         我们上面讲了那么多,我们还是给出桥接模式的经典实现方案吧。不然我们讲的那么多,大家可能对具体的代码的实现并不是很清楚,怎么样才能实现满足桥接模式的方

案呢?

        我们这里还是以上面讲述的ORM的扩展为例来说明这样的方案的可行性和优缺点。具体的代码如下:

                  1、我们先给出抽象的接口,我们给出ORM最终的ORM接口:

   public interface IORM
   {
       ISave Save
       {
           get;
           set;
       }

       IDelete Delete
       {
           get;
           set;
       }

       ICreate Create
       {
           get;
           set;
       }

       ICache Cache
       {
           get;
           set;
       }

       IQuery Query
       {
           get;
           set;
       }

       void Test();

   }

   具体对象的变化因素的抽象接口定义

   public interface ICreate
   {
       bool Create(object o);
   }

    public interface ISave
    {
        bool Save(object o);
    }

   public  interface IDelete
   {
       int Delete(int ID);
   }

具体的ORM类的实现方案:

public class ORM : IORM
    {
        public ISave Save
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public IDelete Delete
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ICreate Create
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ICache Cache
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public IQuery Query
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void Test()
        {
            ///测试缓存对象!
            this.Cache.Cache(new object());
        }
    }


具体的调用是通过接口来进行调用的,所以我们给出几个简单的变化因素的实现类。

   public  class Delete : IDelete
    {
        public int Delete(int ID)
        {
            return 0;
        }
    }

   public  class Create : ICreate
    {
      
        public bool Create(object o)
        {
            return true;
        }
    }

             我们来看看具体的调用代码:

    public class Factory
    {
        public static T Create<T>() where T:class,new()
        {
            T target = new T();

            return target;
        }
    }

   class Program
    {
        static void Main(string[] args)
        {
            IORM orm = new ORM();
            orm.Create = Factory.Create<Create>();
            orm.Cache = Factory.Create<Cache>();
            orm.Test();
        }
    }

对于上面的实现思路,可能大家还不太明白,那么我贴出来变化因素与具体的对象之间的关系吧。

 image我们这里假设有这么多变化的因素会影响到抽象类内部的行为,我们通过抽象的形式来解耦抽象类和变化因素之间的关

系,来达到双方都能独立的发生变化。

image我们通过抽象每个变化因素,让抽象类依赖与接口,然后我们通过属性注入的方式,或者构造函数注入的方式来注入到实

现这个抽象类的具体类的内部。来完成组合对象的初始化操作。

我们来看看最后的处理情况如下:

image我们这里通过构造函数注入,或者属性注入的方式来把需要使用的具体的变化因素注

入到我们具体实现类中,完成对象的所有功能。

六、桥接模式的其他方案

         上面给出了桥接模式的经典模式,那么我们是不是可以有改进的方案呢?我们是不是可以结合之前讲解的一些模式的经验来探讨一下呢?答案是可以的,我们可以采用配

置文件的形式来把具体实现的内容动态的注入到桥接模式中,我们来看看图形话的描述吧,来说明我这里给出的实现思路。

         image

         这里具体的实现代码我本篇就不贴出来了,具体的实现代码下篇开篇的时候,给出,由于本篇篇幅有限,下篇将会展开讲述这块的内容,希望大家见谅。

七、桥接模式使用总结

         通过上面的简单描述,我们知道了桥接模式主要是为了解决,一个对象的多个维度的变化因素的变化太快,难以控制的问题,我们通过将每个维度的变化因素进行抽象,

然后我们的对象只要依赖于抽象即可,具体的实现调用我们不关心,通过对象组合的方式,我们就能组合出我们想要的对象。无疑这是一种非常灵活的也是满足设计模式的原则

的,抽象和实现分离,使他们各自发生变化都不受对方的影响。而且我们也讲述了,使用桥接模式的几个典型的场景,现在我们的实际项目中就有这样的问题,我也是在项目的

使用过程中加深对桥接模式的理解的,桥接模式为系统在多个维度的变化的适应性方面提供了很好的参考,特别适合底层框架的开发过程中使用,可以适应不同变化因素的改

变,当然每个人对模式的理解深度和方向不同,由于本人水平有限,如果有错误之处,还请大伙多多指点,如果有好的建议和意见,请留言,我会细细品位,谢谢大家支持!

八、系列进度

创建型

        1、系统架构技能之设计模式-单件模式

        2、系统架构技能之设计模式-工厂模式

        3、系统架构技能之设计模式-抽象工厂模式

        4、系统架构技能之设计模式-创建者模式

        5、系统架构技能之设计模式-原型模式

结构型

        1、系统架构技能之设计模式-组合模式

        2、系统架构技能之设计模式-外观模式

        3、系统架构技能之设计模式-适配器模式

        4、系统架构技能之设计模式-桥模式

        5、系统架构技能之设计模式-装饰模式

        6、系统架构技能之设计模式-享元模式

        7、系统架构技能之设计模式-代理模式

行为型

        1、系统架构技能之设计模式-命令模式

        2、系统架构技能之设计模式-观察者模式

        3、系统架构技能之设计模式-策略模式

        4、系统架构技能之设计模式-职责模式

        5、系统架构技能之设计模式-模板模式

        6、系统架构技能之设计模式-中介者模式

        7、系统架构技能之设计模式-解释器模式

九、下篇预告

          下篇我们将会开始讲述结构型模式中的装饰模式,这个模式也是平时我们很常用的类,仿佛就是给某个现有的类穿了一件衣服,或者外面包装了个外壳,很有意思的一个模式,我们平时

在游戏开发的过程中,用到这个模式的情况估计会比较多吧,我们下篇将会讲述这个模式,请大家继续支持我!谢谢!您的支持是我前进的动力!。

posted @ 2010-12-10 13:00 何戈洲 阅读(...) 评论(...) 编辑 收藏