假如我們又這樣軟件系統,我們希望它能夠和一個新的庫搭配使用,但是這個庫所提供的接口與我們的軟件系統不兼容,我們不想改變現有代碼就能解決這個問題,怎么辦?這個時候我們就需要將這個新的庫接口轉換成我們所需要的接口,這就是適配器模式設計動機。
一、模式定義
何謂適配器模式?適配器模式就是將一個類的接口,轉換成客戶期望的另一個接口。適配器讓原本接口不兼容的類可以合作無間。
在適配器模式中,我們可以定義一個包裝類,包裝不兼容接口的對象,這個包裝類就是適配器,它所包裝的對象就是適配者。
適配器提供給客戶需要的接口,適配器的實現就是將客戶的請求轉換成對適配者的相應的接口的引用。也就是說,當客戶調用適配器的方法時,適配器方法內部將調用適配者的方法,客戶並不是直接訪問適配者的,而是通過調用適配器方法訪問適配者。因為適配器可以使互不兼容的類能夠“合作愉快”。
二、模式結構

這個適配器模式充滿着良好的OO設計原則:使用對象組合,以修改的接口包裝別適配者。而且這樣做還有一個優點,被適配者的任何子類,都可以搭配適配器使用。
適配器模式有如下四個角色:
Target:目標抽象類
Adapter:適配器類
Adaptee:適配者類
Client:客戶類
三、模式實現
在這里我們模擬一個機器人。首先我們擁有一個機器人,它可以叫(cry),跑(run)。現在我們希望它能夠像小狗一樣叫,像小狗一樣跑。
首先我們需要一個機器人接口:Robot.java。提供cry()和run()方法。
1 public interface Robot { 2 void cry(); 3 4 void move(); 5 }
然后是一個仿生機器人:BioRobot.java。它可以叫和慢慢跑
1 public class BioRobot implements Robot{ 2 3 public void cry() { 4 System.out.println("仿生機器人叫....."); 5 } 6 7 public void move() { 8 System.out.println("仿生機器人慢慢移動...."); 9 } 10 11 }
然后是小狗:Dog.java。可以旺旺叫和快快跑。
1 public class Dog { 2 public void wang(){ 3 System.out.println("小狗叫:汪汪....."); 4 } 5 6 public void run(){ 7 System.out.println("小狗快快跑......"); 8 } 9 }
輪到重點啦:適配器,這個適配器能夠使仿生機器人像狗一樣叫,一樣跑。
1 public class DogAdapter implements Robot{ 2 Dog dog; 3 public DogAdapter(Dog dog){ //取得要適配的對象的引用 4 this.dog = dog; 5 } 6 7 /** 8 * 實現接口中的方法,只需要在相應的方法間進行轉換即可完成。 9 */ 10 public void cry() { 11 System.out.println("機器人模擬狗叫..."); 12 dog.wang(); 13 } 14 15 public void move() { 16 System.out.println("機器人模擬狗跑..."); 17 dog.run(); 18 } 19 20 }
客戶端:Client.java
1 public class Client { 2 public static void main(String[] args) { 3 BioRobot robot = new BioRobot(); //首先我們需要一個機器人 4 Dog dog = new Dog(); //和一只狗 5 6 //將這只狗包裝到機器人中,使其有點兒像機器人 7 Robot dogRobot = new DogAdapter(dog); 8 9 //然后是機器人叫和跑 10 System.out.println("BioRob cry....."); 11 dogRobot.cry(); 12 dogRobot.move(); 13 } 14 }
運行結果。

如果我們希望這個仿生機器人能夠像鳥一樣唧唧的叫和飛呢?只需要添加一個能夠叫和飛的鳥類以及一個鳥的適配器即可。如下:
1 public class Bird { 2 public void jiji(){ 3 System.out.println("唧唧.........."); 4 } 5 6 public void fly(){ 7 System.out.println("我在飛........"); 8 } 9 }
1 public class BirdAdapter implements Robot{ 2 Bird bird; 3 public BirdAdapter(Bird bird){ 4 this.bird = bird; 5 } 6 public void cry() { 7 bird.jiji(); 8 } 9 10 public void move() { 11 bird.fly(); 12 } 13 }
四、模式優缺點
優點
1. 將目標類和適配者類解耦,通過使用適配器讓不兼容的接口變成了兼容,讓客戶從實現的接口解耦。
2. 增加了類的透明性和復用性,將具體的實現封裝在適配者類中,對於客戶端類來說是透明的,而且提高了適配者的復用性。
3. 靈活性和擴展性都非常好在不修改原有代碼的基礎上增加新的適配器類,符合“開閉原則”。
五、使用場景
1. 系統需要使用現有的類,而這些類的接口不符合系統的需要。
2.想要建立一個可以重復使用的類,用於與一些彼此之間沒有太大關聯的一些類
六、模式擴充
適配器模式分為對象適配器和類適配器。前面所概述的是對象適配器。為什么沒有將類適配器呢?因為類適配器需要使用多重繼承,這個在java中沒有辦法實現的。但是當我們遇到可以使用多重繼承語言的時候,還是可能會遇到這種需求。
類適配器模式UML圖。

在類適配器中,由於適配器是適配者的子類,所以可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。但是對於一些不支持多重繼承的編程語言來說(Java ,C#),其使用就會存在一定的局限性:不能將一個適配者類和它的子類都適配到目標接口。
對於對象適配器而言,一個對象適配器可以把多個不同的適配者適配到同一個目標,也就是說,同一個適配器可以把適配者類和它的子類都適配到目標接口。但是如果我們需要置換掉適配者的某些方法時,這個實現就會比較難,我們首先要先做一個適配者的子類,將適配者類的方法置換掉,然后再把適配者類的子類當做真正的適配者進行適配,實現過程較為復雜。
七、總結
1、當我們需要使用的一個現有的類,但是他的接口並不符合我們的需求時,我們可以使用適配器模式。
2、適配器模式分為類適配器和對象適配器,其中類適配器需要用到多重繼承。
