對於適配器相信不會陌生,生活中的例子比比皆是,像耳機轉接線,充電器適配器,水管適配接口等等。經過類比很容易理解軟件中的適配器模式。java
客戶端須要一個target(目標)接口,可是不能直接重用已經存在的adaptee(適配者)類,由於它的接口和target接口不一致,因此須要adapter(適配器)將adaptee轉換爲target接口。前提是target接口和已存在的適配者adaptee類所作的事情是相同或類似,只是接口不一樣且都不易修改。若是在設計之初,最好不要考慮這種設計模式。凡事都有例外,就是設計新系統的時候考慮使用第三方組件,由於不必爲了迎合第三方組件修改本身的軟件設計風格,能夠嘗試使用適配器模式。數據庫
下面是一個很是典型的使用適配器模式的場景:編程
Sun公司在1996年公開了Java語言的數據庫鏈接工具JDBC,JDBC使得Java語言程序可以與數據庫鏈接,並使用SQL語言來查詢和操做數據。JDBC給出一個客戶端通用的抽象接口,每個具體數據庫廠商(如SQL Server、Oracle、MySQL等)的JDBC驅動軟件都是一個介於JDBC接口和數據庫引擎接口之間的適配器軟件。抽象的JDBC接口和各個數據庫引擎API之間都須要相應的適配器軟件,這就是爲各個不一樣數據庫引擎準備的驅動程序。設計模式
另一個比較典型的適配器場景J2EE規範與J2EE規範實現的服務器。SUN公司提供了一套J2EE規範,而後不一樣廠商根據本身的理解實現了不一樣的應用服務器。SUN公司提供了一套servlet api規範,而後實現這套規範的著名應用服務器有Apache Tomcat、Jetty、Oracle 的 Weblogic、IBM 的 WebSphere 等。api
適配器模式的UML類圖以下服務器
從類圖上看主要包含以下角色:ide
類適配器模式(class adapter pattern)
經過繼承進行適配(類間繼承)。類適配器模式在編譯時實現target(目標)接口。這種適配器模式使用了多個實現了期待的接口或者已經存在的接口的多態接口。比較典型的就是:target接口被建立爲一個純粹的接口,Java不支持多繼承的語言。工具
Target:Target目標角色,該角色定義把其餘類轉換爲什麼種接口,也就是指望接口,一般狀況下是一個接口或一個抽象類,通常不會是實現類測試
public interface Target { public void request(); }
Adaptee:Adaptee源角色,想把誰轉換爲目標角色,這個「誰」就是源角色,它是已經存在的、運行良好的類或對象this
public class Adaptee { public void specificRequest() { System.out.println("我是已經存在的運行良好的第三方廠商"); } }
Adapter:Adapter適配器角色,是適配器模式的核心角色,它的職責是經過繼承或是類關聯的方式把源角色轉換爲目標角色
public class Adapter extends Adaptee implements Target { @Override public void request() { super.specificRequest(); } }
ConcreteTarget:目標角色的實現類
public class ConcreteTarget implements Target { @Override public void request() { System.out.println("沒有增長適配器的我方普通實現邏輯"); } }
類適配器模式測試代碼
public class Client { public static void main(String[] args) { //原有無適配器的業務邏輯 Target target = new ConcreteTarget(); target.request(); //增長適配器後的業務邏輯 Target target2 = new Adapter(); target2.request(); } }
對象適配器模式(object adapter pattern)
經過對象層次的關聯關係進行委託(對象的合成關係/關聯關係)。對象適配器模式在運行時實現target(目標)接口。在這種適配器模式中,適配器包裝了一個類實例。在這種狀況下,適配器調用包裝對象實例的方法。
Target:客戶所期待的接口。目標能夠是具體的或抽象的類,也能夠是接口
public class Target { public void request() { System.out.println("沒有適配器的普通請求"); } }
Adaptee:須要適配的類
public class Adaptee { public void specificRequest() { System.out.println("適配器類實現的特殊請求"); } }
Adapter:經過在內部包裝一個Adaptee對象,把源接口轉換成目標接口
public class Adapter extends Target { private Adaptee adaptee = new Adaptee(); @Override public void request() { //替換原理的邏輯,調用適配類的邏輯 adaptee.specificRequest(); } }
對象適配器模式測試代碼
public class Client { public static void main(String[] args) { Target target = new Adapter(); target.request(); } }
缺省適配器模式(default adapter pattern),也叫默認適配器模式、接口適配器模式
當不須要所有實現接口提供的方法時,能夠設計一個適配器抽象類實現接口,併爲接口中的每一個方法提供默認方法實現或者空實現(若是你們作過GUI編程,就能夠常常遇到這種實現,特別是各類控件的事件監聽都提供了適配器類),抽象類的子類就能夠有選擇的覆蓋父類的某些方法實現需求,它適用於一個接口不想使用全部的方法的狀況。在java8後,接口中能夠有default方法,就不須要這種缺省適配器模式了。接口中方法都設置爲default,實現爲空,這樣一樣一樣能夠達到缺省適配器模式一樣的效果。
target:包含了不少沒有實現的操做接口
public interface Target { public abstract void operation1(); public abstract void operation2(); public abstract void operation3(); }
Adapter:默認實現了全部操做抽象類,只是全部的實現都是空實現
public abstract class DefaultAdapter implements Target{ @Override public void operation1() { } @Override public void operation2() { } @Override public void operation3() { } }
測試缺省適配器模式須要用到的類(至關於GUI編程的一個組件,好比按鈕Button)
public class Operator { private Target target; public void addOperation(Target target) { this.target= target; } public void operation1() { target.operation1(); } public void operation2() { target.operation2(); } public void operation3() { target.operation3(); } }
缺省適配器模式測試代碼
public class Client{ public static void main(String[] args) { // 原來要實現全部操做類的操做 Operator operator1= new Operator(); operator1.addOperation(new Target() { @Override public void operation1() {} @Override public void operation2() { System.out.println("invoke operation2"); } @Override public void operation3() {} }); operator1.operation2(); // 二、使用缺省適配器只須要實現須要用到的接口方法 Operator operator2 = new Operator(); operator2.addOperation(new DefaultAdapter() { @Override public void operation2() { System.out.println("invoke operation2"); } }); operator2.operation2(); } }
適配器模式本質上是現有的不兼容的接口轉換爲須要的接口。類適配器模式以繼承現有類的方式轉換;對象適配器模式以聚合對象實例的方式轉換;接口適配器模式以實現接口的方式轉換。適配器模式是在現有的類和系統都不易修改的狀況下才使用,在系統設計之初慎用該設計模式。