設計模式之ADAPTER

設計模式之ADAPTER

設計模式之ADAPTER有兩種實現方式組合(composition)和繼承(inheritance).

日常生活中的適配器

  適配器的例子在日常生活中隨處可見。 例如:中國的電源電壓為 220V ,而日本的電源電壓 110V ,在國內使用日本原裝電器時,就必須有一個電源適配器將 220V 的電壓適配至 110V 。

新的電腦滑鼠一般都是 USB 接口,而舊的電腦機箱上根本就沒有 USB 接口,而只有一個 PS2 接口,這時就必須有一個 PS2 轉 USB 的適配器,將 PS2 接口適配為 USB 接口。

一般家庭中電源插座有的是兩個孔(兩項式)的,也有三個孔(三項式)的。很多時候我們可能更多地使用三個引腳的插頭,但是那種兩孔的插座就不能滿足我們的需求,此時我們一般會買一個拖線板,該拖線板的插頭是是兩腳插頭,這樣就可以插入原先的兩孔插座,同時拖線板上帶有很多兩孔、三孔的插座!這樣不僅可以擴容,更主要的是將兩孔的插座轉變為三孔的插座。

圖1為一個將220v 電源轉換為5v 電源的適配器,圖2為將PS2 接頭轉換為 USB 接頭的適配器,,圖3二孔插座適配為三孔插座的插座適配器。

設計模式之ADAPTER設計模式之ADAPTER

圖 1 電源適配器 圖 2 滑鼠適配器

設計模式之ADAPTER設計模式之ADAPTER

圖 3 插座適配器

軟體工程中的套用

如果你有一個存在的系統需要插入一個新的類庫,但是新的類庫並不能匹配你寫的系統,如下圖:

設計模式之ADAPTER

現在你不想更改存在的舊系統,新的類庫也不能修改,這時候我們就需要寫一個適配器了,用這個適配器來適配新類庫的接口。如下圖:

設計模式之ADAPTER設計模式之ADAPTER

用了適配器之後的整個系統,如下圖:

設計模式之ADAPTER設計模式之ADAPTER

如何使用

實現Adapter方式,有兩種方式:組合(composition)和繼承(inheritance).

假設我們要打樁,有兩種類:方形樁,圓形樁.
public class SquarePeg{
public void insertSquare(String str){
System.out.println("SquarePeg insertSquare():" +str);
}

}

public class RoundPeg{
public void insertRound(String msg){
System.out.println("RoundPeg insertRound():" +msg);
}
}

現在有一個套用 ,需要圓樁能當方樁來打.那么我們需要將這兩個沒有關係的類綜合套用.假設RoundPeg我們沒有原始碼,或原始碼我們不想修改,那么我們使用Adapter來實現這個套用:

public class PegAdapter extends SquarePeg {

private RoundPeg roundPeg;

public PegAdapter(RoundPeg peg) {
this.roundPeg = peg;
}

public void insertSquare(String str) {
roundPeg.insertRound(str);
}
}

在上面代碼中 ,RoundPeg屬於Adaptee,是被適配者.PegAdapter是Adapter,將Adaptee(被適配者RoundPeg)和target(目標SquarePeg)進行適配.實際上這是將組合方法(composition)和繼承(inheritance)方法綜合運用.

PegAdapter首先繼承SquarePeg,然後使用new的組合生成對象方式,生成RoundPeg的對象roundPeg,再重載父類insertSquare()方法。從這裡,你也了解使用new生成對象和使用extends繼承生成對象的不同,前者無需對原來的類修改,甚至無需要知道其內部結構和原始碼.

如果你有些 Java使用的經驗,已經發現,這種模式經常使用。

定義

轉換一個類的接口為客戶端所需要的接口,將兩個不兼容的糾合在一起使用,這個轉換的類就是適配器類。它屬於結構型模式,需要有Adaptee(被適配者)和Adaptor(適配器)兩個身份.它的宗旨就是,保留現有類所提供的服務,向客戶提供接口,以滿足客戶的期望。

結構

Adapter模式有兩種結構,分別是類的適配與對象的適配。

1、類的適配

設計模式之ADAPTER設計模式之ADAPTER

角色分析:

Target目標角色:這是一個接口,定義了客戶所期望的操作 Adaptee源角色:這是我們原有的產品,也是需要被適配的產品。

Adapter適配器角色:在Target目標角色與Adaptee源角色之間提供一種過渡,即把Adaptee源角色所提供的接口轉換為Target目標角色所提供的接口。 從結構圖中可以很容易的知道,適配器角色Adapter必須要繼承Targe目標角色(一個接口)與源角色Adaptee。

源碼:

public class Adaptee{

public void specialRequest(){

System.out.println("Called SpecificRequest() in Adaptee ");

}

}

public interface Target{

public void request();

}

public class Adapter extends Adaptee implements Target{

public void request(){

this.specialRequest();

}

}

2、對象的適配

設計模式之ADAPTER設計模式之ADAPTER

兩種結構的角色其實都是一樣的,客戶的調用流程也是相同的。不同之處於兩者在包裝Adaptee源角色時,前者(類適配)包裝的是Adaptee類(因為它同時從Target與Adaptee繼承而來,可想而知,類適配的Adatper必須是一個具體類,而Target只能是一個接口),後者(對象適配)則直接包裝了一個源Adaptee的實例。這一點如果放在代碼中,則更容易體現出來。此處的差別導致了在具體實現時各個角色的不同實現方式(以類還是以接口)。

讓我們來看一下前面講的生活中的適配器例子的插座對象適配圖:

設計模式之ADAPTER設計模式之ADAPTER

源碼: Adaptee和Target類同上。

public class Adapter implements Target{

Adaptee adaptee = new Adaptee();

public void request(){

adaptee.specialRequest();

}

}

3,類適配和對象適配的不同

設計模式之ADAPTER設計模式之ADAPTER

為何使用

我們經常碰到要將兩個沒有關係的類組合在一起使用,第一解決方案是:修改各自類的接口,但是如果我們沒有原始碼,或者,我們不願意為了一個套用而修改各自的接口。 怎么辦? 使用 Adapter,在這兩種接口之間創建一個混合接口(混血兒).

1、 系統需要使用現有的類,而此類的接口不符合系統的需要。
2、 想要建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起 工作。這些源類不一定有很複雜的接口。
3、 (對對象適配器而言)在設計里,需要改變多個已有子類的接口,如果使用類的適配器模式,就要針對每一個子類做一個適配器,而這不太實際.

幾個要點

1、 Adapter模式主要套用於“希望服用一些現存的類,但是接口又與復用環境要求不一致的情況”,在遺留代碼復用、類庫遷移等方面非常有用。

2、 Gof23定義了兩種 Adapter模式的實現結構:對象適配器和類適配器。但類適配器採用“多繼承”的實現方式 帶來了不良的高耦合,所以一般不推薦使用。對象適配器採用“對象組合”的方式,更符合松耦合精神。

3、 Adapter模式本身要求我們儘可能的使用“面向接口的編程”風格,這樣才能在後期很方便的適配。

進一步使用

上面的PegAdapter是繼承了SquarePeg,如果我們需要兩邊繼承,即繼承SquarePeg 又繼承RoundPeg,因為Java中不允許多繼承,但是我們可以實現(implements)兩個接口(interface)

public interface IRoundPeg{
public void insertRound(String msg);
}

public interface ISquarePeg{
public void insertSquare(String str);

}

下面是新的 RoundPeg 和SquarePeg, 除了實現接口這一區別,和上面的沒什麼區別。
public class SquarePeg implements ISquarePeg{
public void insertSquare(String str){
System.out.println("SquarePeg insertSquare():"+ str);
}

}

public class RoundPeg implements IRoundPeg{
public void insertRound(String msg){
System.out.println("RoundPeg insertRound():"+msg);
}
}

下面是新的 PegAdapter,叫做two-way adapter:

public class PegAdapter implements IRoundPeg,ISquarePeg{
private RoundPeg roundPeg;

private SquarePeg squarePeg;

// 構造方法
public PegAdapter(RoundPeg peg) {
this.roundPeg = peg;
}

// 構造方法
public PegAdapter(SquarePeg peg) {
this.squarePeg = peg;
}

public void insertSquare(String str) {
roundPeg.insertRound(str);
}

public void insertRound(String msg) {
squarePeg.insertSquare(msg);
}

}

還有一種叫 Pluggable Adapters,可以動態的獲取幾個adapters中一個。使用Reflection技術,可以動態的發現類中的Public方法。

現實中的套用

在JDK1.0和1.1版本里沒有Java聚集(Collectcion)的框架,這一框架是在JDK1.2版本中給出的。與引起相對應,JDK1.0和1.1版本里提供了Enumeration接口,而JDK1.2版本給出了Iterator接口。如果有很多的Java代碼是為老版本Java編譯器寫的,使用的是Enumeration,現在想使用新版本編譯器和新的Java聚集庫包的話,需要將已有代碼的Iterator接口轉換成Enumeration接口。因為Java聚集要求的Iterator接口,只有這樣才能使已有的代碼可以使用新版本的聚集對象。反之,需要將舊代碼的Enumeration接口轉換成Iterator接口。

Enumeration接口類圖:

<<interface>>

Enumeration

hasMoreElements ()

nextElement ()

Iterator接口類圖:

<<interface>>

Iterator

hasnext ()

next ()

remove ()

適配Enumeration成為Iterator:

設計模式之ADAPTER設計模式之ADAPTER

適配Enumeration成為Iterator的原始碼:

public class EnumerationIterator implements Iterator
{
Enumeration enum;
public EnumerationIterator(Enumeration enum){
this.enum=enum;
}
public boolean hasNext(){
return enum.hasMoreElements();
}
public Object next(){
return enum.nextElement();
}
public void remove(){
throw new UnsupportedOperationException();
}
}

在架構層次上的套用

1,JDBC驅動軟體與適配器模式

Sun Microsystem在1996年公開了Java語言的資料庫連線工具JDBC。JDBC使得Java語言程式能夠連線資料庫上,並使用SQL(Strucured Query Language)來查詢和修改數據和數據定義。

JDBC給出一個客戶端通用的界面。每個資料庫引擎的JDBC驅動軟體都是一個介於JDBC接口和資料庫引擎接口之間的適配器軟體,如下圖所示。

設計模式之ADAPTER設計模式之ADAPTER

抽象的JDBC接口和各個資料庫引擎的API之間都需要相應的適配器軟體,即為各個資料庫引擎準備的驅動軟體。

2,JDBC/ODBC橋樑

JDBC的想法與微軟的ODBC想法很接近。只要有合適的驅動軟體,JDBC就可以直接連線到資料庫上。如果沒有合適的JDBC驅動軟體,用戶也可以通過ODBC驅動軟體把JDBC通過一個JDBC/ODBC橋樑軟體與ODBC驅動軟體連線起來,從而達到連線資料庫的目的。ODBC/JDBC橋樑的架構圖如下圖所示。

設計模式之ADAPTER設計模式之ADAPTER

JDBC庫不可能和ODBC的庫有相同的接口,因此,使用適配器模式將ODBC的API接口改為JDBC的接口就是唯一可行的方法。因此,JDBC/ODBC橋樑是適配器模式的具體套用。

相關詞條

相關搜尋

熱門詞條

聯絡我們