设计模式学习笔记十一:适配器模式(Adapter Pattern)
 
     1.概述
    在软件开发中,我们经常会遇到系统间集成,在系统集成时,最常见的问题就是系统间的接口不一致。很多能够满足功能的系统模块,由于接口不一致,导致无法使用。例如,常用的媒体播放器是MS Media player和RealPlayer,他们的文件结构和软件接口完全不同,前者支持WMF格式的音频和视频,后者支持RM格式的音频和视频。如果我们希望自己的软件能够播放这两种播放器播放相应格式的音频和视频,我们该怎么办呢?一切从头开始,重写一个支持这两种格式的播放软件?呵呵,你要不觉得累你就重写了,呵呵。
    适配器模式(Adapter):将一个的接口转化成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的的那些类可以一起工作。 适配器模式的意图就是使接口不兼容的类能够一起工作,通常情况下,这些接口不兼容的类在逻辑上的功能是一致或相似的。在开发中,系统的数据和行为都正确,但接口不符时,我们就应该考虑用适配器模式,目的就在于使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要用于希望服用一些现有的类,但是接口又与环境要求不一致的情况。
    适配器有类适配器和对象适配器两种类型,二者的意图相同,只是实现的方法和适用的情况不同。类适配器采用继承来实现,对象适配器则采用组合的方法来实现。
    类适配器:类适配器通过多重继承对一个接口与另一个接口进行匹配,其结构如下图所示:
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程

    Target定义了Client使用的与特定领域相关的接口,Client通过调用Target实现某一特定的操作。Adaptee是一个已经存在的类,需要与Target协同工作,这个接口需要适配。Adapter适配器适配Adaptee和Target接口。在类适配器中,通过继承获得Adaptee中的方法:
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_02
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    /// <summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04    /// Adaptee
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    /// </summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Adaptee
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public void Execute()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        { 
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    /// <summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04    /// Target
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    /// </summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Target
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public void Do()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        { 
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    /// <summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04    /// Adapter,.NET中不支持多继承
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    /// </summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Adapter : Adaptee, Target
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_28        Target 成员
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
    在.NET中类适配器使用受到一定的限制,由于.NET不支持多继承,即一个类只能有一个父类。因此当Target是一个类,而不是一个接口时就无法实现类适配器,这时需要使用对象适配器。
    对象适配器:对象适配器采用对象组合,通过引用一个类与另一个类接口,其结构下图所示:
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_30
    在对象适配器中,通过组合获得Adaptee对象:
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_02
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    /// <summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04    /// Adaptee
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    /// </summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Adaptee
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public void Execute()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        { 
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    /// <summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04    /// Target
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    /// </summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Target
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public void Do()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        { 
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    /// <summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04    /// Adapter,.NET中不支持多继承
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    /// </summary>
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Adapter :  Target
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        private Adaptee adaptee;
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public Adapter(Adaptee ap)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            adaptee = ap;
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_28        Target 成员
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
    使用场合:需要使用一个已经存在的类,但接口与设计要求不符;希望创建一个可以复用的类,该类可以与其他不相关的类或者是将来不可预见的类协同工作。在双方多不太容易修改的时候再使用适配器模式适配。
     2.实例
    大话设计模式中的篮球翻译适配器,先来看结构图:
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_64

    代码如下:
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_02
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    //篮球运动员
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public abstract class Player
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        protected string name;
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public Player(string name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            this.name = name;
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public abstract void Attack();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public abstract void Defense();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    //前锋
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Forwards : Player
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public Forwards(string name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            : base(name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Attack()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("前锋 {0} 进攻", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Defense()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("前锋 {0} 防守", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    //中锋
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Center : Player
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public Center(string name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            : base(name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Attack()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("中锋 {0} 进攻", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Defense()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("中锋 {0} 防守", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    //后卫
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Guards : Player
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public Guards(string name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            : base(name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Attack()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("后卫 {0} 进攻", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Defense()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("后卫 {0} 防守", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    //外籍中锋
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class ForeignCenter
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        private string name;
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public string Name
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09            get { return name; }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09            set { name = value; }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public void 进攻()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("外籍中锋 {0} 进攻", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public void 防守()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.WriteLine("外籍中锋 {0} 防守", name);
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    //翻译者
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06    public class Translator : Player
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03    {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        private ForeignCenter wjzf = new ForeignCenter();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public Translator(string name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            : base(name)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            wjzf.Name = name;
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Attack()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            wjzf.进攻();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04        public override void Defense()
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_09        {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            wjzf.防守();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_10        }
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05    }

调用代码:
 
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_06         static   void  Main( string [] args)
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_03         {
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Player b = new Forwards("巴蒂尔");
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            b.Attack();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Player m = new Guards("麦克格雷迪");
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            m.Attack();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            //Player ym = new Center("姚明");
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Player ym = new Translator("姚明");
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            ym.Attack();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            ym.Defense();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_04            Console.Read();
设计模式学习笔记十一:适配器模式(Adapter Pattern)_编程_05        }
 
结果显示:
前锋 巴蒂尔 进攻
后卫 麦克格雷迪 进攻
外籍中锋 姚明 进攻
外籍中锋 姚明 防守
    有了翻译者(适配器),姚明不懂英文,火箭的教练和球员也不用学中文就可以使火箭整个团队沟通合作的很好。哈哈。
     3.总结
    1).NET中的适配器模式DataAdapter。DataAdapter用作DataSet和数据源之间的适配器以便检索和保存数据。DataAdapter通过映射Fill(更改了DataSet中的数据以便与数据源中的数据相匹配)和Update(更改了数据源中的数据以便与DataSet中的数据相匹配),来提供这一适配器。

    实现要点
    Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用;Adapter模式有对象适配器和类适配器两种形式的实现结构,但是类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神;Adapter模式的实现可以非常的灵活,不必拘泥于GOF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的;Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便的适配。

    效果:
    对于类适配器:用一个具体的Adapter类对Adaptee和Taget进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作;使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类;仅仅引入了一个对象,并不需要额外的指针一间接得到Adaptee.
    对于对象适配器:允许一个Adapter与多个Adaptee,即Adaptee本身以及它的所有子类(如果有子类的话)同时工作。Adapter也可以一次给所有的Adaptee添加功能。使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
    适用性
    在以下各种情况下使用适配器模式:系统需要使用现有的类,而此类的接口不符合系统的需要;想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口;(对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
     在对两个无关类进行适配的时候考虑一下适配的代价,一个非常庞大的适配器可能会对系统性能有影响。