一、概念 將一個類的介面轉換成客戶希望的另外一個介面,適配器模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。 二、模式動機 適配器的本質就是轉換類型(源介面的功能和目標介面的功能相同或者相近,如此轉換才有意義),目的就是復用已有的功能。 三、模式的結構 適配器跟據實現方式可以分為類適配 ...
一、概念
將一個類的介面轉換成客戶希望的另外一個介面,適配器模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
二、模式動機
適配器的本質就是轉換類型(源介面的功能和目標介面的功能相同或者相近,如此轉換才有意義),目的就是復用已有的功能。
三、模式的結構
適配器跟據實現方式可以分為類適配器和對像適配器,簡單結構圖如下:
(類適配器) (對象適配器)
類適配器:
1.Target:期待得到的目標介面,由於是類適配器,所以這個必須是Interface
2.Adaptee:被適配的介面,是一個具體類
3.Adapter:適配器,具體類
類適配器的實現思路是,通過繼承被適配器的類Adaptee,實現目標介面Target。由於繼承是靜態關係,所以他只能適配當前的類Adaptee,而不能適配Adaptee的其它子類。由於是繼承關係,適配器類可以很方便的重寫被適配器類中的方法。
類適配器的樣例代碼如下:
package adapter.classpattern; /** * 期望的目標介面 * @ClassName: Target * @author beteman6988 * @date 2017年11月11日 下午11:20:44 * */ public interface Target { /** * 源類也有的方法 * @Title: sampleOperation1 * @param * @return void * @throws */ public void sampleOperation1(); /** * 源類中沒有的方法 * @Title: sampleOperation2 * @param * @return void * @throws */ public void sampleOperation2(); }
package adapter.classpattern; /** * 被適配的類 * @ClassName: Adaptee * @author beteman6988 * @date 2017年11月11日 下午11:23:22 * */ public class Adaptee { public void sampleOperation1() { //Do something...... } }
package adapter.classpattern; public class Adapter extends Adaptee implements Target { /** * 目標介面需要,但是源類中沒有的方法,需自已恰當的實現 */ @Override public void sampleOperation2() { // TODO Auto-generated method stub } }
對象適配器:
1.Target:期待得到的目標介面,可以是具體類或者抽象類
2.Adaptee:被適配的介面
3.Adapter:適配器,具體類
對象適配器的實現思路是:適配器類(Adapter)繼承或實現目標介面(Target),適配器類含有被適配器介面的對象成員, 適配器類通過委派,調用被適配對象的方法以實現復用,達到實現目標介面到源介面(被適配器介面)的轉換。
對象適配器,由於引用的是被適配介面的對象,所以他可以適配被適配介面的所有子類或者實現,但是無法覆蓋被適配介面的方法,只能適過一個子類繼承適配器介面,改寫父類的方法,然後適配器類適配這個子類的方法來實現。
樣例代碼如下:
package adapter.objpattern; /** * 期望的目標介面 * @ClassName: Target * @author beteman6988 * @date 2017年11月11日 下午11:20:44 * */ public interface Target { /** * 目標介面的方法 * @Title: sampleOperation1 * @param * @return void * @throws */ public void sampleOperation1(); /** * 目標介面的方法 * @Title: sampleOperation2 * @param * @return void * @throws */ public void sampleOperation2(); }
package adapter.objpattern; /** * 被適配的類 * @ClassName: Adaptee * @author beteman6988 * @date 2017年11月11日 下午11:23:22 * */ public class Adaptee { public void sampleOperationA() { System.out.println("適配到-Adaptee.sampleOperationA()"); } }
package adapter.objpattern; public class Adapter extends Adaptee implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee=adaptee; } /** * 目標介面需要的方法 */ @Override public void sampleOperation1() { /** * 通過委派復用被適配對象的方法 */ this.adaptee.sampleOperationA(); } /** * 目標介面需要,但被適配對象不中具備的方法 */ @Override public void sampleOperation2() { //DO Something...... System.out.println("目標介面需要,但被適配對象中不具備的方法,需恰當的實現。。。。。。"); } }
package adapter.objpattern; public class Client { public static void main(String[] args) { Adaptee adaptee=new Adaptee(); //被適配的源對象 Target target=new Adapter(adaptee); //將源對象適配到目標介面的適配器 target.sampleOperation1(); //目標介面調用自已的方法,以期待源對象來實現。 target.sampleOperation2(); } }
運行結果如下:
適配到-Adaptee.sampleOperationA()
目標介面需要,但被適配對象中不具備的方法,需恰當的實現。。。。。。
四、預設適配器
預設適配器就是用一個抽像類繼承或實現目標介面,對目標介面中的所有方法提供平庸實現,從該抽象類繼承的子類,只需要覆寫父類中所需要關註的方法。而不需要實現目標介面的所有方法。比如一個按鈕事件的介面,所有按鈕都支持單擊和雙擊事件,但是現在有一個按鈕只需要對單擊事件做出響應,這時這個按鈕就和目標期待的介面不匹配,如果要匹配目標介面,這個按鈕事件對象就必須對雙擊事件也要做出響應,用預設適配器方法就能很好解決這個問題。首先為這個按鈕事件介面提供一個抽象類,抽象類平庸實現(什麼事都不做)對單擊和雙擊事件的響應,然後只支持單擊事件響應的按鈕繼承於這個抽象類即可,然後只需重寫抽象類中的單擊按鈕響應方法即可。
五、模式樣例
就以日常電視的電源插座為位,目前只有一個二孔的插座,而電視插頭是三插頭,如何讓電視的三孔插頭插到兩孔的插座上,使電視能正常工作,就需要一個兩空到三空的轉換插座,即就是本例中的轉換器Adapter,下麵以程式的方式說明整個轉換過程。
類模式:
兩孔到三孔的插座轉換器繼承於兩孔插座,表示TwoToThreeSocketAdapter是特殊的TwoProngedSocket,他具有TwoProngedSocket的兩個極(正極和負極)的功能,且多了一個地極,代碼如下:
package adapter.classpattern.sample; /** * 兩孔插座 * @ClassName: TwoProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:32:40 * */ public class TwoProngedSocket { /** * 負極插空 * @Title: requestNegativePole * @param * @return void * @throws */ public void requestNegativePole() { System.out.println("兩孔插座的負極插空開始工作。。。。。。"); } /** * 正極插空 * @Title: requestPositivePole * @param * @return void * @throws */ public void requestPositivePole() { System.out.println("兩孔插座的正極插空。。。。。。"); } }
package adapter.classpattern.sample; /** * 電視機需要的三插孔插座介面 * @ClassName: ThreeProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:37:58 * */ public interface ThreeProngedSocket { /** * 負極插孔 * @Title: requestNegativePole * @param * @return void * @throws */ public void requestNegativePole(); /** * 正極插孔 * @Title: requestPositivePole * @param * @return void * @throws */ public void requestPositivePole(); /** * 地極插孔 * @Title: requestEarthPole * @param * @return void * @throws */ public void requestEarthPole(); }
package adapter.classpattern.sample; /** * 兩孔到三孔的插座轉換器,對外是三孔插座(implements ThreeProngedSocket)或者說具備了三孔插座的功能, * 卻是一個特殊的兩孔插座(extends TwoProngedSocket) * @ClassName: TwoToThreeSocketAdapter * @author beteman6988 * @date 2017年11月12日 下午9:47:01 * */ public class TwoToThreeSocketAdapter extends TwoProngedSocket implements ThreeProngedSocket{ /** * 兩孔插座不具備的第三極 地極接地 */ @Override public void requestEarthPole() { System.out.println("地極插空接地開始工作。。。。。。"); } }
package adapter.classpattern.sample; /** * 電視類,具備播放功能 * @ClassName: TV * @author beteman6988 * @date 2017年11月12日 下午10:00:15 * */ public class TV { /** * 電視播放時,需要一個三孔插坐,或者具備三孔插座功能(implements ThreeProngedSocket)的插座 * @Title: play * @param @param socket * @return void * @throws */ public void play(ThreeProngedSocket socket) { socket.requestNegativePole(); //負極能正常工作 socket.requestPositivePole(); //正極能正常工作 socket.requestEarthPole(); //地極能正常工作 System.out.println("電視正在播放。。。。。"); } public static void main(String[] args) { TV tv=new TV(); //目前房間只有一個兩孔插座,和一個兩孔到三孔的插座轉的換器,那麼轉換器插到兩孔插座上,上面的三孔插座即可供電視機用。 ThreeProngedSocket socket=new TwoToThreeSocketAdapter(); //給電視提供一個具有三孔插座功能的轉換器 tv.play(socket); } }
運行結果如下:
兩孔插座的負極插空開始工作。。。。。。
兩孔插座的正極插空。。。。。。
地極插空接地開始工作。。。。。。
電視正在播放。。。。。
對像模式:
三孔插座轉換器想具備對外提供電力的功能,必須給他提供一個真實的兩孔插座對象,當對他請求正負極時,通過委派給真實的兩孔插座的正負極對外提供電力。
package adapter.objpattern.sample; /** * 兩孔插座 * @ClassName: TwoProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:32:40 * */ public class TwoProngedSocket { /** * 負極插孔 * @Title: requestNegativePole * @param * @return void * @throws */ public String requestNegativePole() { return "兩孔插座的負極插空開始工作。。。。。。"; } /** * 正極插孔 * @Title: requestPositivePole * @param * @return void * @throws */ public String requestPositivePole() { return "兩孔插座的正極插空。。。。。。" ; } }
package adapter.objpattern.sample; /** * 電視機需要的三插空插座介面 * @ClassName: ThreeProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:37:58 * */ public interface ThreeProngedSocket { /** * 負極插孔 * @Title: requestNegativePole * @param * @return void * @throws */ public void requestNegativePole(); /** * 正極插孔 * @Title: requestPositivePole * @param * @return void * @throws */ public void requestPositivePole(); /** * 地極插孔 * @Title: requestEarthPole * @param * @return void * @throws */ public void requestEarthPole(); }
package adapter.objpattern.sample; /** * 兩孔到三孔的插座轉換器,對外是三孔插座(implements ThreeProngedSocket)或者說具備了三孔插座的功能, * 但是他如果真實具備三孔插作的功能對外提供電力,則必須給他提供一個具有電力的兩孔插作(TwoProngedSocket)對象 * 他的正負極通過委派這個兩孔插座對象的正負極對外提供電力,地極則自已實現接地 * @ClassName: TwoToThreeSocketAdapter * @author beteman6988 * @date 2017年11月12日 下午9:47:01 * */ public class TwoToThreeSocketAdapter implements ThreeProngedSocket{ //具有電力功能的兩孔插座 private TwoProngedSocket twoProngedSocket; /** * 給三也插座轉換器提供具有電力功能的兩孔插座 * @param twoProngedSocket */ public TwoToThreeSocketAdapter(TwoProngedSocket twoProngedSocket) { this.twoProngedSocket=twoProngedSocket; } /** * 兩孔插座不具備的第三極 地極接地 */ @Override public void requestEarthPole() { System.out.println("地極插空接地開始工作。。。。。。"); } @Override public void requestNegativePole() { System.out.println("委派"+twoProngedSocket.requestNegativePole()); } @Override public void requestPositivePole() { System.out.println("委派"+twoProngedSocket.requestPositivePole()); } }
package adapter.objpattern.sample; /** * 電視類,具備播放功能 * @ClassName: TV * @author beteman6988 * @date 2017年11月12日 下午10:00:15 * */ public class TV { /** * 電視播放時,需要一個三孔插坐,或者具備三孔插座功能(implements ThreeProngedSocket)的插座 * @Title: play * @param @param socket * @return void * @throws */ public void play(ThreeProngedSocket socket) { socket.requestNegativePole(); //負極能正常工作 socket.requestPositivePole(); //正極能正常工作 socket.requestEarthPole(); //地極能正常工作 System.out.println("電視正在播放。。。。。"); } public static void main(String[] args) { TV tv=new TV(); //兩孔插座 TwoProngedSocket twoProngedSocket=new TwoProngedSocket(); //給三空插座轉換器提供一個兩孔插座,讓他具備三孔插座的功能 ThreeProngedSocket socket=new TwoToThreeSocketAdapter(twoProngedSocket); //給電視提供一個具有三孔插座功能的轉換器 tv.play(socket); } }
運行結果如下:
委派兩孔插座的負極插空開始工作。。。。。。
委派兩孔插座的正極插空。。。。。。
地極插空接地開始工作。。。。。。
電視正在播放。。。。。
六、模式優缺點
優點:更好的復用性,如果已存的類功能已經有了,只是與目標介面不相容,通過使用適配器,就可以讓已有的功能得到更好的復用。
缺點:過多的使用適配器,會讓系統非常的凌亂,不易整體把握。如果可行的話,不如直接引用目標對象。