天天看點

接口轉換--擴充卡模式

對于擴充卡模式的四字概括,稱為“接口轉換”。

這裡的接口,并不特指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設計模式》

好了,就寫到這,休息了,關于對象擴充卡和類擴充卡的比較,大家可以思考一下。

繼續閱讀