一、適配器模式的定義
適配器模式(Adapter)的定義如下:將一個類的接口轉換成客戶希望的另外一個接口,使得原本由於接口不兼容而不能一起工作的那些類能一起工作。
適配器模式分為類結構型模式和對象結構型模式兩種:在類適配器模式中,適配器與適配者之間是繼承(或實現)關系;在對象適配器模式中,適配器與適配者之間是關聯關系。前者類之間的耦合度比后者高,且要求程序員了解現有組件庫中的相關組件的內部結構,所以應用相對較少些。
二、適配器模式優缺點
主要優點:
- 將目標類和適配者類解耦,通過引入一個適配器類來重用現有的適配者類,無須修改原有結構。
- 增加了類的透明性和復用性,將具體的業務實現過程封裝在適配者類中,對於客戶端類而言是透明的,而且提高了適配者的復用性,同一個適配者類可以在多個不同的系統中復用。
- 靈活性和擴展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”。
具體來說,類適配器模式還有如下優點:
- 由於適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。
對象適配器模式還有如下優點:
- 一個對象適配器可以把多個不同的適配者適配到同一個目標;
- 可以適配一個適配者的子類,由於適配器和適配者之間是關聯關系,根據“里氏代換原則”,適配者的子類也可通過該適配器進行適配。
類適配器模式的缺點如下:
- 對於Java、C#等不支持多重類繼承的語言,一次最多只能適配一個適配者類,不能同時適配多個適配者;
- 適配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類;
- 在Java、C#等語言中,類適配器模式中的目標抽象類只能為接口,不能為類,其使用有一定的局限性。
對象適配器模式的缺點如下:
- 與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當做真正的適配者進行適配,實現過程較為復雜。
三、適配器模式實現
- 類適配器模式可采用定義一個適配器類來實現當前系統的業務接口,同時又繼承現有組件庫中已經存在的組件。
- 對象適配器模式可釆用將現有組件庫中已經實現的組件引入適配器類中,該類同時實現當前系統的業務接口。現在來介紹它們的基本結構。
適配器模式(Adapter)包含以下主要角色。
- 目標(Target)接口:當前系統業務所期待的接口,它可以是抽象類或接口。
- 適配者(Adaptee)類:它是被訪問和適配的現存組件庫中的組件接口。
- 適配器(Adapter)類:它是一個轉換器,通過繼承或引用適配者的對象,把適配者接口轉換成目標接口,讓客戶按目標接口的格式訪問適配者。
類適配器模式的結構圖如圖所示(繼承或實現關系):
對象適配器模式的結構圖如圖所示(關聯關系):
代碼實現(類適配器模式代碼):
//目標接口 interface Target { public void request(); }
//適配者接口 class Adaptee { public void specificRequest() { System.out.println("適配者中的業務代碼被調用!"); } }
//類適配器類 class ClassAdapter extends Adaptee implements Target { public void request() { specificRequest(); } }
//客戶端代碼 public class ClassAdapterTest { public static void main(String[] args) { System.out.println("類適配器模式測試:"); Target target = new ClassAdapter(); target.request(); } }
測試結果如下:
類適配器模式測試:
適配者中的業務代碼被調用!
代碼實現(對象適配器模式代碼):
//目標接口 interface Target { public void request(); } //適配者接口 class Adaptee { public void specificRequest() { System.out.println("適配者中的業務代碼被調用!"); } } //對象適配器類 class ObjectAdapter implements Target { private Adaptee adaptee; public ObjectAdapter(Adaptee adaptee) { this.adaptee=adaptee; } public void request() { adaptee.specificRequest(); } } //客戶端代碼 public class ObjectAdapterTest { public static void main(String[] args) { System.out.println("對象適配器模式測試:"); Adaptee adaptee = new Adaptee(); Target target = new ObjectAdapter(adaptee); target.request(); } }
四、適配器模式擴展(雙向適配器模式)
適配器模式(Adapter)可擴展為雙向適配器模式,雙向適配器類既可以把適配者接口轉換成目標接口,也可以把目標接口轉換成適配者接口,其結構圖如圖所示:
實現代碼如下:
//目標接口 interface TwoWayTarget { public void request(); } //適配者接口 interface TwoWayAdaptee { public void specificRequest(); } //目標實現 class TargetRealize implements TwoWayTarget { public void request() { System.out.println("目標代碼被調用!"); } } //適配者實現 class AdapteeRealize implements TwoWayAdaptee { public void specificRequest() { System.out.println("適配者代碼被調用!"); } } //雙向適配器 class TwoWayAdapter implements TwoWayTarget, TwoWayAdaptee { private TwoWayTarget target; private TwoWayAdaptee adaptee; public TwoWayAdapter(TwoWayTarget target) { this.target=target; } public TwoWayAdapter(TwoWayAdaptee adaptee) { this.adaptee=adaptee; } public void request() { adaptee.specificRequest(); } public void specificRequest() { target.request(); } } //客戶端代碼 public class TwoWayAdapterTest { public static void main(String[] args) { System.out.println("目標通過雙向適配器訪問適配者:"); TwoWayAdaptee adaptee=new AdapteeRealize(); TwoWayTarget target=new TwoWayAdapter(adaptee); target.request(); System.out.println("-------------------"); System.out.println("適配者通過雙向適配器訪問目標:"); target=new TargetRealize(); adaptee=new TwoWayAdapter(target); adaptee.specificRequest(); } }
運行結果如下:
目標通過雙向適配器訪問適配者: 適配者代碼被調用! ------------------- 適配者通過雙向適配器訪問目標: 目標代碼被調用!
五、適配器模式適用場景
常見適配器模式使用場景如下:
- 系統需要使用一些現有的類,而這些類的接口(如方法名)不符合系統的需要,甚至沒有這些類的源代碼。
- 想創建一個可以重復使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。
在開源框架中有很多地方使用到了適配器模式,比如Spring AOP中,使用的 Advice(通知) 來增強被代理類的功能;SpringMVC中適配器模式主要用於執行目標 Controller
中的請求處理方法;Spring中關於數據庫類型的適配;Netty中關於InboundHandler和OutboundHandler的適配。