【23种设计模式】结构型模式 > 适配器模式

来源:互联网 发布:阿里云短信发送 c 编辑:程序博客网 时间:2024/05/16 06:52

适配器模式(Adapter Pattern)

用来消除不兼容性。我的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在220V的电压下工作?答案是引入一个电源适配器(AC Adapter),俗称充电器或变压器,有了这个电源适配器,生活用电和笔记本电脑即可兼容(重要点在于,电脑工作电压和民用电压都不能被我们修改,所以我们引入了一个适配器,在不修改电脑工作电压和民用电压的情况下,使得电脑获得了20V 输入电压,这就是适配器模式的关键,即被适配的两方不能被修改)。在软件开发中,有时也存在类似这种不兼容的情况,我们也可以像引入一个电源适配器一样引入一个称之为适配器的角色来协调这些存在不兼容的结构,这种设计方案即为适配器模式。

一、介绍

适配器模式有对象适配器类适配器两种实现。在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。在实际开发中,对象适配器的使用频率更高。


二、适用场景

      在以下情况下可以考虑使用适配器模式:
       (1) 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
       (2) 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

三、对象适配器

3.1 UML类图


在对象适配器模式结构图中包含如下几个角色:
       ● Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
       ● Adapter(适配器类)也被称为包装类:适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
       ● Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
比如我在上家公司新写了一个棋牌游戏控件的类库,相比公司原有的麻将控件更加灵活,我暴露出来的接口也和原先的接口很不一样,但是由于旧的控件接口已经用在了很多地方,要全部改成新接口也不太现实,这时候我写的新接口就需要去适配旧的接口。在这里,我写的新接口就是适配者类Adaptee,公司旧有的接口就是适配的目标Target,而我还需要写一个适配器Adapter,用新的接口来实现旧有的接口,也就是适配的目标。这样公司的以前的代码就不需要修改接口,仍旧采用旧的调用形式,但是接口的实现是用我写的新接口实现的。
适配器模式的核心是Adapter(适配器类)的编写,典型的对象适配器类是:
C++ 
class Adapter : public Target // 继承Target类{private :    Adaptee adaptee; //维持一个对适配者对象的引用 ,关联Adaptee类public:     Adapter(Adaptee adaptee) {        this->adaptee = adaptee;    }    void request() {        adaptee.specificRequest(); //转发调用      }};

3.2 代码实现

C++ 
#include <iostream>using namespace std; // 抽象类class Target{public:     virtual ~Target() {};    // 目标方法    virtual void request() = 0; };class Adaptee{public:    // 被适配的方法    void specificRequest()    {        cout << "specificRequest()" << endl;    }};class Adapter : public Target{public:    Adapter()    {        p = Adaptee();    }    // 目标方法    void request()    {        // 调用被适配的方法        p.specificRequest();    }private:     Adaptee p; };int main(){    Target& t = Adapter();    t.request();}

C#
using System;using System.Collections.Generic;class Program{    interface Target    {        // 目标接口        void request();     }    class Adaptee    {        // 被适配接口        public void specificRequest()        {            Console.WriteLine("specificRequest()");        }    }    class Adapter : Target    {        private Adaptee ad = new Adaptee();  // 关联到Adaptee        public void request()        {            ad.specificRequest();        }    }        public static void Main()    {        Target t = new Adapter();        t.request();    }} 
结果:
specificRequest()

四、类适配器

4.1 UML类图


类适配器和对象适配器的组成元素一样,唯一的区别是Adapter针对Adaptee,从关联关系变成了继承关系,并把Target写成了接口(因为C#中类只能单继承)。

4.2 代码实现

C++
#include <iostream>using namespace std; // 抽象类class Target{public:     virtual ~Target() {};    // 目标方法    virtual void request() = 0; };class Adaptee{public:    // 被适配的方法    void specificRequest()    {        cout << "specificRequest()" << endl;    }};class Adapter : public Target, public Adaptee // C++实现中只在Adapter类和对象适配器有区别{public:    Adapter()    {    }    // 目标方法    void request()    {        // 调用被适配的方法        specificRequest();    }};int main(){    Target& t = Adapter();    t.request();}
C#
using System;using System.Collections.Generic;class Program{    interface Target    {        // 目标接口        void request();     }    class Adaptee    {        // 被适配接口        public void specificRequest()        {            Console.WriteLine("specificRequest()");        }    }    class Adapter : Adaptee, Target    {        public void request()        {            specificRequest();        }    }        public static void Main()    {        Target t = new Adapter();        t.request();    }}

五、对象适配器和类适配器的比较

类适配器:
  •  用一个具体的Adapter类对Adaptee和Target进行匹配,结果是当我们想要匹配一个类以及所有他的子类的时候,类Adapter将不能胜任。
  •  使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。
  •  仅仅引用了一个对象,并不需要额外的指针以间接得到Adapteee。
  •  类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。
对象适配器:
  •  允许一个Adapter与多个Adaptee(Adaptee本身和他的所有子类)同时工作,Adaptee也可以一次给所有的Adaptee添加功能。
  •  使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身

六、优缺点

6.1 优点

       无论是对象适配器模式还是类适配器模式都具有如下优点:

       (1) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。

       (2) 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。

       (3) 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

      具体来说,类适配器模式还有如下优点:

      由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

      对象适配器模式还有如下优点:

      (1) 一个对象适配器可以把多个不同的适配者适配到同一个目标

      (2) 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。


6.2 缺点

     类适配器模式的缺点如下:

      (1) 对于JavaC#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者

      (2) 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;

      (3) JavaC#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

      对象适配器模式的缺点如下:

      与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。


阅读全文
0 0
原创粉丝点击