天天看点

接口转换--适配器模式

对于适配器模式的四字概括,称为“接口转换”。

这里的接口,并不特指java中的Interface. 而是一个广义的概念,接口的概念可以理解为:一个程序,提供给外界可以访问的某个类的某个方法。接口以其唯一的命名为外界所知晓。唯一的命名包括全类名,及方法名,如全类名com.pattern.adapter.A,方法名doSth().

所谓接口转换,就是说将如上的类转换成其他类,将方法名改成其他的名称(也可能只改类名不改方法名),如类名改为com.pattern.adapter.B, 方法名改为doOther()。为什么要这样做?究竟怎么转换?请看下面例子。

假设我们的程序为客户提供某个接口,以供客户实现某种功能。我们称之为目标接口。我们与客户协商后决定,此目标接口将命名为com.pattern.adapter.Target(Target可能是一个Class或Interface,依据依赖抽象的原则,Target应尽可能是一个泛类型),方法名为doSth()。那么客户的程序中,将会从我们的程序得到一个Target对象,并调用此对象的doSth方法以实现某些功能。而我们的程序要做的,就是传给客户程序这样的对象。

由于历史原因,现在我们的程序中有一个类com.pattern.adapter.Stubborn,提供了方法do() ,此方法实现了与Target中doSth()方法所期望的相同的功能。我们希望提供给客户一个Stubborn对象,并让客户调用do()方法实现其功能。这样的话,客户端代码就得修改,比如客户端代码本来是这样的:

                        Target t = getTarget(); //getTarget方法由我们提供

                        t.doSth();

现在我们要求客户改为:

                        Stubborn s = getStubborn(); //getStubborn方法由我们提供

                        s.do();

但这显然不太现实,我们的程序应该具有良好的兼容性,而不是要求客户修改其代码。那么我们要怎么做?

如果我们可以将Stubborn变成Target,那不就OK了。

如果当初设计的时候,把Stubborn设计成Target的一个子类,或者实现类,现在就不需要这么麻烦了。这样Stubborn对象其实就是Target对象,也不会出现我们现在遇到的问题了。可他偏偏就不是。

我们何不现在就把Stubborn修改一下,让他继承Target(或实现Target,取决于Target是接口还是类),可以是可以,但是,根据开闭原则,我们应该尽量增加新的,而不是修改已有的。

好,我们的方案是这样的:

1.不修改Stubborn。

2.增加一个新类,叫做com.pattern.adapter.Adapter. 让Adapter继承(或实现)Target,重写doSth()方法,并且Adapter持有一个Stubborn对象的引用。重写的doSth()方法调用Stubborn对象的do()方法。

3.创建Adapter对象,传给客户。

以下是Target,Stubborn,Adapter三个类型的源码:

//

public Interface Target

{

    public void doSth();

}

//

public class Stubborn

{

     public void do()

     {

             //do something

     }

}

//

public class Adapter implements Target

{

      Stubborn origin;

      public Adapter(Stubborn origin)

      {

           this.origin = origin;

       }

      @Override

      public void doSth()

      {

             origin.do();

       }

 }

这样,我们只需要传给客户端一个Adapter对象(客户端只知道他是一个Target对象,并不知道他是一个Adapter对象,他也不需要知道),而客户端的代码不需要做任何改变。

还记得前面客户端的代码是怎么写的吗? :

         Target  t = getTarget();  

getTarget()是我们提供的方法,以下是此方法的重载实现:

         public Target getTarget()

         {

                 return new Adapter(new Stubborn());

         }

至此,我们的适配器模式实现了将Stubborn转换成Target。

以下是我们例子中适配器模式的类图(为了使其看起来更符合适配器模式,我们将Stubborn的名称改为Adaptee,表示被适配):

接口转换--适配器模式

其实,适配器模式有两种类型,我们刚才这个例子叫做对象适配器,还有一种叫做类适配器。

1. 对象适配器是将被适配的类型以对象组合的方式被适配器所引用,如我们刚才的做法是在Adapter类中,定义一个Stubborn类型的成员origin,然后doSth方法的实现其实是调用origin的do()方法。

2.类适配器是让适配器继承被适配的类型(即让Adapter继承Stubborn),具体做法情况如下Adapter类的类适配器实现源码:

  public class Adapter implements Target extends Stubborn

  {

         @Override

         public void doSth()

         {

               do();  //do方法继承自Stubborn

          }

  }

请将两种方式中Adapter类的实现相比较。

类适配器对于java来说,有一个问题是如果Target也是Class而不是Interface,那么Adapter就需要双继承,这在java就做不到了。

以下是适配器模式的定义:

     “适配器模式将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间”     ---引用自《Head First设计模式》

好了,就写到这,休息了,关于对象适配器和类适配器的比较,大家可以思考一下。

继续阅读