標籤:設計模式 適配器模式 android
適配器模式
從名字上就可以看出適配器是為了針對介面不匹配的情況,而做出的相容方法,
假設我們有一個已經存在的類Adaptee,其中有一些已經存在並實現好的方法methodA。但是客戶不希望調用這個類,而是希望調用一個特定的介面例如Target介面。
於是如果想要調用Adaptee.methodA()方法,建立一個Adapter類實現客戶要求的Target介面,Target介面中假設客戶希望調用callMethodA()方法來實現Adaptee.methodA()方法功能。能夠想到的就是以下兩種方式:
類的適配器模式
讓Adapter類實現Target介面介面同時繼承Adaptee類,這樣Adapter類就繼承了Adaptee.methodA(),在其callMethodA()中調用父類的methodA()方法即可。客戶建立Adapter類對象,就可以通過Target介面調用Adapter.methodA()。
類圖
代碼實現等待被適配的Adaptee
public class Adaptee { public void methodA(){ System.out.println("I am the methodA in Adaptee"); }}
客戶期待的Target介面
public interface Target { public void callMethodA(); public void otherMethod();}
繼承了Adaptee並實現Target介面的適配者
public class Adapter extends Adaptee implements Target { @Override public void callMethodA() { super.methodA(); } @Override public void otherMethod() { System.out.println("I am the otherMethod in Adapter"); }}
客戶類測試
public class Client { public static void main(String[] args) { Adapter mAdapter = new Adapter(); mAdapter.callMethodA(); mAdapter.otherMethod(); }}
輸出結果
I am the methodA in AdapteeI am the otherMethod in Adapter
對象的適配器模式
讓Adapter類中持有一個Adaptee類的執行個體,在Adapter類實現Target介面的methodA()方法時,在其中調用Adaptee執行個體的methodA()方法即可。
類圖
代碼實現
在上面的類的適配器基礎上,只需要修改Adapter和客戶類即可。
繼承了Adaptee並實現Target介面的適配者
public class Adapter implements Target { private Adaptee mAdaptee; public Adapter(Adaptee mAdaptee) { this.mAdaptee = mAdaptee; } @Override public void callMethodA() { this.mAdaptee.methodA(); } @Override public void otherMethod() { System.out.println("I am the otherMethod in Adapter"); }}
客戶類測試
public class Client { public static void main(String[] args) { Adaptee mAdaptee=new Adaptee(); Adapter mAdapter = new Adapter(mAdaptee); mAdapter.callMethodA(); mAdapter.otherMethod(); }}
輸出結果也是一樣的
輸出結果
I am the methodA in AdapteeI am the otherMethod in Adapter
兩種適配器的對比
兩種模式各有利弊,類的適配器模式已經繼承了Adaptee類,就沒法再繼承其他的類,這時它的弊端,不過好處就是邏輯比較清晰,適合只適配單一的Adaptee的情況。對象的適配器模式則沒有繼承的限制,採用彙總的方式來適配待適配的類。個人個傾向於對象的適配器模式這種適配器方式,耦合性更低。
預設適配器模式
適配器還有第三種模式,看起來和上面的兩種適配器模式不太一樣,
對於一個定義了較多方法的介面,我們實現該介面的時候,一來實現這麼多的方法很麻煩。二來有些方法我們並不需要,都實現了反而混淆了重點。
為瞭解決這個問題,就是用一個抽象類別繼承該介面,並在抽象類別中使用空實現來實現介面中的方法。(也不一定,有些情況抽象類別中會有介面中的方法的預設實現,而繼承它的子類中只需要完成其特有的一些方法,或者在原有方法的基礎上進行增加。例如Android中的PhoneBase類,BaseAdapter類等)。
類圖代碼實現
public interface Target { public void A(); public void B(); public void C(); public void D(); public void E(); public void F();}public abstract class AbstractTarget implements Target { @Override public void A() { } //可以留一個B方法不實現,B還是抽象的,留給子類必須實現 @Override public void C() { System.out.println("I am C in AbstractTarget"); } @Override public void D() { } @Override public void E() { } @Override public void F() { }}public class ConcreteTarget extends AbstractTarget { @Override public void B() {//B是必須實現的 System.out.println("I am B in ConcreteTarget"); } @Override public void A() {//選擇性的重寫A和C方法 System.out.println("I am A in ConcreteTarget"); } @Override public void C() { super.C(); System.out.println("I am C in ConcreteTarget"); }}
測試
public static void main(String[] args) { Target mTarget = new ConcreteTarget(); mTarget.A(); mTarget.B(); mTarget.C(); }
Android中的適配器模式
Android中最常見的適配器模式就屬於Android中的Adapter了,其應用融合了預設適配器模式和對象的適配器模式
抽象類別BaseAdapter實現ListAdapter和SpinnerAdapter兩個介面,而這兩個介面都是繼承自Adapter介面,這兩個介面分別又定義了自己的方法
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter
不過BaseAdapter中並未實現介面中的全部方法
SimpleAdapter則繼承了BaseAdapter,並實現了所有沒實現的介面方法,有些BaseAdapter已經實現的方法,並沒有實現。
public class SimpleAdapter extends BaseAdapter
這裡就是上面的預設適配器模式。
那麼對象的適配器呢,
當我們使用SimpleAdapter時,例如:
ListView.setAdapter(new SimpleAdapter(this, getData(path), android.R.layout.simple_list_item_1, new String[] { "title" }, new int[] { android.R.id.text1 }));
先看一下SimpleAdapter建構函式
public SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)
其中第2個參數是一個Map的隊列,對應每一個List的Item的資料,第3個參數則是展開這個Item的View的layout資源的整形ID。第4個參數from數組是第2個參數的Map的key,最後一個參數to數組則是from參數的每一個key對應的資料放置在哪個組件裡。
在ListView調用onMeasure確定組件尺寸時,就會調用到mAdapter.getCount(),調用這個SimpleAdapter的getCount()函數:
public int getCount() { return mData.size(); } public Object getItem(int position) { return mData.get(position); }
間接調用之前建立SimpleAdapter時建構函式傳遞的data資料。
在繪製的時候,則會調用
mAdapter.getView(position, null, this)
擷取適配器所適配的資料所對應的View來進行繪製。
所以這裡SimpleAdapter就是對data對象的對象適配器模式,如果不使用適配器,ListView直接持有data資料當然也可以得到資料。但是ListView使用Adapter的精髓就是不需要去管資料是什麼,只需要建立不同的Adapter,就可以做出不同效果,含有不同組件的ListView。
Android中的設計模式-適配器模式