代码改变世界

C#设计模式(适配器模式)

2011-09-29 16:50  田志良  阅读(...)  评论(... 编辑 收藏

  众所周知,在中国通用的电压时 220V,而美国电压则是 110V,如果有经常在美国和中国之间跑的 IT 人,而其笔记本都是随身携带的,那么它的笔记本的电压问题如何解决呢?(因为在美国和中国电压不同,所以一般的电器会不通用的)而适配器在这个问题上体现得淋漓尽致。现在的笔记本都有一个电源适配器,而正是这个电源适配器来解决上面提到的适配器问题,比如,一款索尼笔记本,其输入电流为交流100V~240V,而输出则是统一的直流 19.5V,在电源适配器的一端输入交流电流,然后通过电源适配器把电源变成需要的电压,也就是适配器的作用是使得一个东西适合另外一个东西。

  下面来给出适配器模式的定义:

  适配器模式将一个接口转换成另外一个接口,以符合客户的期望。

  主要用于以下情况:

  1. 比如现在有一个旧的软件系统,其中有一个组件已经过时了,更新需用到第三方的组件(新组件),但是旧组件的接口和新组件的接口不同,同时,您又不想去改变现有的代码,此时可使用适配器模式。您可以通过适配器模式将新组件的一些接口转换成为你所期望的接口,这样就无需要改变原来的代码,轻松实现从旧组件更新到新组件了。
  2. 比如一个 WINE 的工具,它允许用户在 Linux 环境下运行 Windows 程序,这也是一种适配器。

  可以这样来理解适配器模式的,适配器模式就是将一些对象包装起来,然后让它们的接口看起来是别的接口。还有需要提及的是,适配器模式分为类适配器模式和对象适配器模式,但是类适配器模式要以多重继承为前提,而 C# 不支持多重继承,所以在这里只介绍对象适配器,如果有对类适配器模式感兴趣的话,可以使用 C++ 来实现一下。

                                                   对象适配器的结构图

  举个例子说明一下:

  我需要使用一个新的组件来替换掉我系统中已经过时了的组件,所以我使用了一个第三方组件,而这个组件当中的接口的名字居然都是用中文写的(这个可能是开发这个组件的程序员的问题所造成的),而我现有的系统中的的旧有的组件的接口中确是通过英文来调用的组件,而由于系统过于复杂,所以我便选择了使用适配器模式来解决这个问题。

                      类图

  Target 类(Target 类代表能够被客户端使用的接口):

namespace Adapter 
{ 
    public abstract class Target 
    { 
        //温度 
        /// <summary> 
        /// 下面的接口才是可以被客户端所识别的接口,也就是目标接口 
        /// 而前面在被适配器类中的中文却不能被客户端识别,需要被适配 
        /// </summary> 
        public abstract void GetTemperature();
        //气压 
        public abstract void GetPressure();
        //湿度 
        public abstract void GetHumidity();
        //紫外线强度 
        public abstract void GetUltraviolet(); 
    } 
}

  被适配的类 Adaptee(Adaptee 中的接口由于不能被客户端识别,所以需要被适配):

using System;
namespace Adapter 
{ 
    class Adaptee 
    { 
        /// <summary> 
        /// 在被适配器类中的接口并不是客户端需要的接口 
        /// 比如这里是使用的中文,而我在客户端却必须要使用英文 
        /// 所以在这里我必须使用适配器来适配 
        /// </summary> 
        public void 得到温度() 
        { 
            Console.WriteLine("您得到了今日的温度"); 
        }
        public void 得到气压() 
        { 
            Console.WriteLine("您得到了今日的气压"); 
        }
        public void 得到湿度() 
        { 
            Console.WriteLine("您得到了今日的湿度"); 
        }
        public void 得到紫外线强度() 
        { 
            Console.WriteLine("您得到了今日的紫外线强度"); 
        } 
    } 
}

  适配器(适配器将不能被客户端识别的接口间接转换为可以被识别的接口):

namespace Adapter 
{ 
    public class Adapter:Target 
    { 
        //在适配器中必须要维护一个被适配器类的对象 
        private Adaptee adaptee = new Adaptee();
        /// <summary> 
        /// 通过适配器来适配原来不能被客户端所认识的接口 
        /// </summary> 
        public override void GetTemperature() 
        { 
            adaptee.得到温度(); 
        }
        public override void GetPressure() 
        { 
            adaptee.得到气压(); 
        }
        public override void GetHumidity() 
        { 
            adaptee.得到湿度(); 
        }
        public override void GetUltraviolet() 
        { 
            adaptee.得到紫外线强度(); 
        } 
    } 
}

  客户端:

using System;
namespace AdapterTest 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            //实例化一个适配器给目标接口 
            Adapter.Target target = new Adapter.Adapter();
            //下面的这些就是客户端可以被识别了接口了
            target.GetTemperature(); 
            target.GetPressure(); 
            target.GetHumidity(); 
            target.GetUltraviolet();
            Console.ReadKey(); 
        } 
    } 
}

  可以看出,上面的适配器就是一个中介人,它把本来客户端的请求转换成了 Adaptee 所代表的接口所能理解的请求。或者说是,把本来客户端不认识的 Adaptee 间接介绍给了客户端认识,不过注意是间接。

  适配器模式在 . NET 中的应用:

  使用过 ADO.NET 等数据访问之类的操作的话,应该都使用过 DataAdapter。DataAdapter 的主要作用是用来在 DataSet 和数据源之间提供一个适配器功能来实现检索和保存数据。因为数据源有可能是 DB2 ,SqlServer ,Oracle 等,而这些数据库的数据在组织上都有一定的差别,所以,它们的接口单单对于 DataSet 来说的话,是有区别的,而且区别还比较大,而我们总不可能针对每一种数据库都使用不同的 DataSet 来保存和检索数据吧,我们希望的是提供一种统一的 DataSet ,而其既可以对 Oracle 使用,又可以对 DB2 等等数据库使用,也就是一种通用的 DataSet 类型。所以在这里面便可以使用适配器模式了,我们在数据源和 DataSet 之间插入一个适配器 DataAdapter ,通过适配器来实现对各种数据库的不同应用(提供给客户端统一接口),而在 DataSet 中,其只能看到 DataAdapter 这一层,对于数据源的话,它是不需要过问的,这样也就是实现了一种通用的 DataSet。