zoukankan      html  css  js  c++  java
  • c#设计模式-适配器模式

    一、 适配器(Adapter)模式

    适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。

    名称由来

    这很像变压器(Adapter),变压器把一种电压变换成另一种电压。美国的生活用电电压是110V,而中国的电压是220V。如果要在中国使用美国电器,就必须有一个能把220V电压转换成110V电压的变压器。这个变压器就是一个Adapter。

    Adapter模式也很像货物的包装过程:被包装的货物的真实样子被包装所掩盖和改变,因此有人把这种模式叫做包装(Wrapper)模式。事实上,大家经常写很多这样的Wrapper类,把已有的一些类包装起来,使之有能满足需要的接口。

    适配器模式的两种形式

    适配器模式有类的适配器模式和对象的适配器模式两种。我们将分别讨论这两种Adapter模式。

    二、 类的Adapter模式的结构:

    由图中可以看出,Adaptee类没有Request方法,而客户期待这个方法。为了使客户能够使用Adaptee类,提供一个中间环节,即类Adapter类,Adapter类实现了Target接口,并继承自Adaptee,Adapter类的Request方法重新封装了Adaptee的SpecificRequest方法,实现了适配的目的。

    因为Adapter与Adaptee是继承的关系,所以这决定了这个适配器模式是类的。

    该适配器模式所涉及的角色包括:

    目标(Target)角色:这是客户所期待的接口。因为C#不支持多继承,所以Target必须是接口,不可以是类。

    源(Adaptee)角色:需要适配的类。

    适配器(Adapter)角色:把源接口转换成目标接口。这一角色必须是类。

    三、 类的Adapter模式示意性实现:

    下面的程序给出了一个类的Adapter模式的示意性的实现:

    //  Class Adapter pattern -- Structural example  
    using System;
    
    // "ITarget"
    interface ITarget
    {
        // Methods
        void Request();
    }
    
    // "Adaptee"
    class Adaptee
    {
        // Methods
        public void SpecificRequest()
        {
            Console.WriteLine("Called SpecificRequest()");
        }
    }
    
    // "Adapter"
    class Adapter : Adaptee, ITarget
    {
        // Implements ITarget interface
        public void Request()
        {
            // Possibly do some data manipulation
            // and then call SpecificRequest
            this.SpecificRequest();
        }
    }
    
    /// <summary>
    /// Client test
    /// </summary>
    public class Client
    {
        public static void Main(string[] args)
        {
            // Create adapter and place a request
            ITarget t = new Adapter();
            t.Request();
        }
    }

    四、 对象的Adapter模式的结构:

    从图中可以看出:客户端需要调用Request方法,而Adaptee没有该方法,为了使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而将客户端与Adaptee衔接起来。由于Adapter与Adaptee是委派关系,这决定了这个适配器模式是对象的。

    该适配器模式所涉及的角色包括:

    目标(Target)角色:这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。

    源(Adaptee)角色:需要适配的类。

    适配器(Adapter)角色:通过在内部包装(Wrap)一个Adaptee对象,把源接口转换成目标接口。

    五、 对象的Adapter模式示意性实现:

    下面的程序给出了一个类的Adapter模式的示意性的实现:

    // Adapter pattern -- Structural example  
    using System;
    
    // "Target"
    class Target
    {
        // Methods
        virtual public void Request()
        {
            // Normal implementation goes here
        }
    }
    
    // "Adapter"
    class Adapter : Target
    {
        // Fields
        private readonly Adaptee _adaptee = new Adaptee();
    
        // Methods
        override public void Request()
        {
            // Possibly do some data manipulation
            // and then call SpecificRequest
            _adaptee.SpecificRequest();
        }
    }
    
    // "Adaptee"
    class Adaptee
    {
        // Methods
        public void SpecificRequest()
        {
            Console.WriteLine("Called SpecificRequest()");
        }
    }
    
    /// <summary>
    /// Client test
    /// </summary>
    public class Client
    {
        public static void Main(string[] args)
        {
            // Create adapter and place a request
            Target t = new Adapter();
            t.Request();
        }
    }

    六、 在什么情况下使用适配器模式

    在以下各种情况下使用适配器模式:

    1. 系统需要使用现有的类,而此类的接口不符合系统的需要。
    2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
    3. (对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。

    七、 一个实际应用Adapter模式的例子

    下面的程序演示了Class Adapter与Object Adapter的应用。

    // Example of implementing the Adapter pattern
    using System;
    
    // Target
    public interface ICar
    {
        void Drive();
    }
    
    // Direct use without Adapter
    public class CToyota : ICar
    {
        public void Drive()
        {
            Console.WriteLine("Vroom Vroom, we're off in our Toyota");
        }
    }
    
    // Adaptee
    public class CCessna
    {
        public void Fly()
        {
            Console.WriteLine("Static runup OK, we're off in our C172");
        }
    }
    
    // Class Adapter
    public class CDrivableCessna : CCessna, ICar
    {
        public void Drive() { base.Fly(); }
    }
    
    // Object Adapter
    public class CDrivableCessna2 : ICar
    {
        private readonly CCessna _mOContained;
    
        public CDrivableCessna2()
        {
            _mOContained = new CCessna();
        }
    
        public void Drive() { _mOContained.Fly(); }
    }
    
    // Client
    public class Client
    {
        public static void Main(string[] args)
        {
            ICar oCar = new CToyota();
    
            Console.Write("Class Adapter: Driving an Automobile");
            oCar.Drive();
            oCar = new CDrivableCessna();
            Console.Write("Driving a Cessna");
            oCar.Drive();
            oCar = new CDrivableCessna2();
            Console.Write(" Object Adapter: Driving a Cessna");
            oCar.Drive();
        }
    }

    八、 关于Adapter模式的讨论

    Adapter模式在实现时有以下这些值得注意的地方:

    1. 目标接口可以省略,模式发生退化。但这种做法看似平庸而并不平庸,它可以使Adaptee不必实现不需要的方法(可以参考Default Adapter模式)。其表现形式就是父类实现缺省方法,而子类只需实现自己独特的方法。这有些像模板(Template)模式。
    2. 适配器类可以是抽象类。
    3. 带参数的适配器模式。使用这种办法,适配器类可以根据参数返还一个合适的实例给客户端。
  • 相关阅读:
    POJ 3259 Wormholes【BellmanFord】
    POJ 2960 SNim【SG函数的应用】
    ZOJ 3578 Matrixdp水题
    HDU 2897 邂逅明下【bash博弈】
    BellmanFord 算法及其优化【转】
    【转】几个Java的网络爬虫
    thinkphp 反字符 去标签 自动加点 去换行 截取字符串 冰糖
    php 二维数组转 json文本 (jquery datagrid 数据格式) 冰糖
    PHP 汉字转拼音(首拼音,所有拼音) 冰糖
    设为首页与加入收藏 兼容firefox 冰糖
  • 原文地址:https://www.cnblogs.com/guyun/p/6183346.html
Copyright © 2011-2022 走看看