设计模式(十一)—适配器模式(结构型)

  • 一、简介(Brief Introduction)

             将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

           Adapter模式的宗旨:保留现有类所提供的服务,向客户提供接口,以满足客户的期望。

           例子1:iphone4,你即可以使用UBS接口连接电脑来充电,假如只有iphone没有电脑,怎么办呢?苹果提供了iphone电源适配器。可以使用这个电源适配器充电。这个iphone的电源适配器就是类似我们说的适配器模式。(电源适配器就是把电源变成需要的电压,也就是适配器的作用是使得一个东西适合另外一个东西。)

           例子2:最典型的例子就是很多功能手机,每一种机型都自带有从电器,有一天自带充电器坏了,而且市场没有这类型充电器可买了。怎么办?万能充电器就可以解决。这个万能充电器就是适配器。

    二、模式分析(Analysis)


    目标角色(Target):— 定义Client使用的与特定领域相关的接口。

    客户角色(Client)与符合Target接口的对象协同。

    被适配角色(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。

    适配器角色(Adapte):适配器模式的核心。它将对被适配Adaptee角色已有的接口转换为目标角色Target匹配的接口。对Adaptee的接口与Target接口进行适配.

    使用的前提:

    1.接口中规定了所有要实现的方法

    2.但一个要实现此接口的具体类,只用到了其中的几个方法,而其它的方法都是没有用的。

    实现方法:

    1.用一个抽象类实现已有的接口,并实现接口中所规定的所有方法,这些方法的实现可以都是“平庸”实现----空方法;但此类中的方法是具体的方法,而不是抽象方法,否则的话,在具体的子类中仍要实现所有的方法,这就失去了适配器本来的作用。

    2.原本要实现接口的子类,只实现中的抽象类即可,并在其内部实现时,只对其感兴趣的方法进行实现。

    注意事项:

    1.充当适配器角色的类就是:实现已有接口的抽象类

    2.为什么要用抽象类:

            此类是不要被实例化的。而只充当适配器的角色,也就为其子类提供了一个共同的接口,但其子类又可以将精力只集中在其感兴趣的地方。

    三、案例分析(Example)

    namespace 适配器
    {

    1、客户端

    class Program
        {
            static void Main(string[] args)
            {
                Player b = new Forwards("巴蒂尔");
                b.Attack();
                Player m = new Guards("雷迪");
                m.Attack();
    
                Player ym = new Translator("姚明");       //翻译者告诉姚明,既要‘进攻’又要‘防守’
                ym.Attack();
                ym.Defence();
    
                Console.Read();
    
            }
        }

    2、球员,前锋、后卫、中锋来继承它
        abstract class Player
        {
            protected string name;
            public Player(string name)
            {
                this.name = name;
            }
            public abstract void Attack();   //进攻和防守方法
            public abstract void Defence();
        }
    3、 前锋、后卫、中锋
        //前锋
        class Forwards : Player
        {
            public Forwards(string name)
                : base(name)
            { }
            public override void Attack()
            {
                Console.WriteLine("前锋{0}进攻", name);
            }
            public override void Defence()
            {
                Console.WriteLine("前锋{0}防守", name);
            }
    
        }
    
        //中锋
        class Center : Player
        {
            public Center(string name)
                : base(name)
            { }
            public override void Attack()
            {
                Console.WriteLine("中锋{0}进攻", name);
            }
            public override void Defence()
            {
                Console.WriteLine("中锋{0}防守", name);
            }
    
        }
    
        //后卫
        class Guards : Player
        {
            public Guards(string name)
                : base(name)
            { }
            public override void Attack()
            {
                Console.WriteLine("中锋{0}进攻", name);
            }
            public override void Defence()
            {
                Console.WriteLine("中锋{0}防守", name);
            }
    
        }
    

    4、外籍 中锋
        //外籍中锋
        class ForeighCenter
        {
            private string name;
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
    
            public void 进攻()               //表明外籍中锋只懂中文‘进攻’
            {
                Console.WriteLine("外籍中锋{0}进攻", name);
            }
    
            public void 防守()
            {
                Console.WriteLine("外籍中锋{0}防守", name);
            }
        }
    

    5、翻译者
        //翻译者
        class Translator : Player
        {
            private ForeighCenter a = new ForeighCenter();  //声明并实例化一个内部‘外籍中锋’对象,
    表明翻译者与外籍球员有关联(私有对象)
            public Translator(string name)
                : base(name)
            {
                a.Name = name;
            }
    
            public override void Attack()  //翻译者把Attack翻译为‘进攻’告诉外籍中锋
            {
                a.进攻();
            }
    
            public override void Defence()
            {
                a.防守();
            }
        }
    }

    四、解决的问题(What To Solve)

              适配器模式为对象提供了一种完全不同的接口。你可以运用适配器(Adapter)来实现一个不同的类的常见接口,同时避免了因升级和拆解客户代码所引起的纠纷。

              适配器模式把一个类的接口变换成客户端所期待的另一种接口, Adapter模式使原本因接口不匹配(或者不兼容)而无法在一起工作的两个类能够在一起工作。又称为转换器模式、变压器模式(把已有的一些类包装起来,使之能有满足需要的接口)。

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

    1 • 你想使用一个已经存在的类,而它的接口不符合你的需求。

    2 • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。

    3 •(仅适用于对象适配器)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。

    五、优缺点(Advantage and Disadvantage)

     优点:

    1.  通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单、更直接、更紧凑。
    2. 复用了现存的类,解决了现存类和复用环境要求不一致的问题。
    3. 将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
    4. 一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

    缺点:

    对于对象适配器来说,更换适配器的实现过程比较复杂。

    六、扩展(Extend)

    (一)共有类适配器模式:1.类的适配器模式(采用继承实现)2.对象适配器(采用对象组合方式实现)

    1类适配器——适配器继承自已实现的类(一般多重继承)。

    Adapter与Adaptee是继承关系

    1、用一个具体的Adapter类和Target进行匹配。结果是当我们想要一个匹配一个类以及所有它的子类时,类Adapter将不能胜任工作

    2、使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子集

    3、仅仅引入一个对象,并不需要额外的指针以间接取得adaptee


    2对象适配器—适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。对象适配器可以适配它的父类接口。即仅仅引入一个对象,并不需要额外的指针以间接取得adaptee。

    Adapter与Adaptee是委托关系

    1、允许一个Adapter与多个Adaptee同时工作。Adapter也可以一次给所有的Adaptee添加功能

    2、使用重定义Adaptee的行为比较困难

    (3) 缺省适配器模式:

    缺省适配器模式是一种特殊的适配器模式,但这个适配器是由一个抽象类实现的,并且在抽象类中要实现目标接口中所规定的所有方法,但很多方法的实现都是“平庸”的实现,也就是说,这些方法都是空方法。而具体的子类都要继承此抽象类。

    无论哪种适配器,它的宗旨都是:保留现有类所提供的服务,向客户提供接口,以满足客户的期望。即在不改变原有系统的基础上,提供新的接口服务。

    (二)如何做到一个类不被实例化或者不被轻易实例化?

    1.把一个类定义为抽象类;

    2.把一个类的构造方法设置为:private类型的,这样在客户端就不能通过new ClassName()方法来轻易将一个类实例化,而要生成此类的实例就必须通过一个特殊的方法,这样在一个系统中,对此类的使用就能得到合理的控制(如:单例模式/多例模式/简单工厂方法等模式)。

    3. 对于两个独立的系统,要满足开—闭原则,则适配器模式会有一定的局限性.

     七、联系(Link)

    桥接模式:桥梁模式与对象适配器类似,但是桥梁模式的出发点不同:桥梁模式目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立的加以改变。而对象适配器模式则意味着改变一个已有对象的接口

    装饰模式:装饰模式增强了其他对象的功能而同时又不改变它的接口。因此装饰模式对应用的透明性比适配器更好。结果是装饰支持递归组合,而纯粹使用适配器是不可能实现这一点的。

    外观模式:适配器模式的重点是改变一个单独类的API。Facade的目的是给由许多对象构成的整个子系统,提供更为简洁的接口。而适配器模式就是封装一个单独类,适配器模式经常用在需要第三方API协同工作的场合,设法把你的代码与第三方库隔离开来。

    适 配器模式与外观模式都是对现相存系统的封装。但这两种模式的意图完全不同,前者使现存系统与正在设计的系统协同工作而后者则为现存系统提供一个更为方便的 访问接口。简单地说,适配器模式为事后设计,而外观模式则必须事前设计,因为系统依靠于外观。总之,适配器模式没有引入新的接口,而外观模式则定义了一个 全新的接口。

    代理模式:在不改变它的接口的条件下,为另一个对象定义了一个代理。

    装饰者模式,适配器模式,外观模式三者之间的区别:

    装饰者模式的话,它并不会改变接口,而是将一个一个的接口进行装饰,也就是添加新的功能。

    适配器模式是将一个接口通过适配来间接转换为另一个接口。

    外观模式的话,其主要是提供一个整洁的一致的接口给客户端。

     八、总结(Summary)

             适配器就是使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值