Adapter 模式


物件之間溝通時介面的不匹配時有所見,也許您在開發應用程式的過程中已設計了一些方法供具某些公開協定的物件使用,您打算引入了先前開發過的類別,或者是第三方的類別,但很常發現到它們之間的公開介面並不一致。

舉個例子來說,在JDK5之後有了個新的foreach語法,預設如果是個陣列或Collection物件,則可以這麼使用:
List<String> list = new ArrayList<String>();
for(String s : list) {
    System.out.println(s);
}

事實上,JDK5所引入的foreach語法,只要是有實作java.lang.Iterable介面的物件,就可以套用,java.lang.Iterable是在JDK5之後新增的介面:
package java.lang;
public interface Iterable<T> {

    Iterator<T> iterator();
}

foreach語法可以簡化程式的撰寫,如果你有個類別,想要能夠參與foreach語法,但本身沒有實作Iterable介面該如何?例如您想要String也可以用foreach語法逐一取出字元的話,那麼您可以使用以下類似的作法:
import java.util.*;

class IterableString implements Iterable<Character> {
private String str;
IterableString(String str) {
this.str = str;
}
public Iterator<Character> iterator() {
return new Iterator<Character>() {
private int index;
public boolean hasNext() {
return index < str.length();
}
public Character next() {
return str.charAt(index++);
}
public void remove() {
throw new RuntimeException("Not supported");
}
};
}
}

public class Main {
public static void main(String[] args) throws Exception {
String original = new String("I like foreach!");
for(char c : new IterableString(original)) {
System.out.println(c);
}
}
}

這是Object Adapter模式的一個實現,以類別圖來表示Object Adapter模式的話:


就上例而言,Iterable就是Target角色,String就是Adaptee的角色,IterableString就是Adapter的角色。

如果使用Python正式地實現Object Adapter,則可以像以下的實作方式:
class Adaptee:
def doAction(self):
return 'Adaptee'

class Adapter:
def __init__(self, adaptee):
self.adaptee = adaptee

def request(self):
return self.adaptee.doAction()

def printIt(x):
print(x.request())

client = Adapter(Adaptee())
printIt(client)

由於Python可以動態地為類別添加屬性,即使是未添加屬性前就已建立的物件,在類別動態添加屬性之後,也可依Python的名稱空間搜尋順序套用上新的屬性,用這種方式,您可以為Adaptee動態地添加公開介面,例如:
class Adaptee:
def doAction(self):
return 'Adaptee'

def printIt(x):
print(x.request())

client = Adaptee()
Adaptee.request = lambda self : self.doAction()

printIt(client)

雖然實現的方式有點巧妙,不過這也是Object Adapter的實現,只不過在添加的方法中包裹原物件的操作。