問題導入:比如有A型螺母和B型螺母,那么用戶可以再A型螺母上直接使用按着A型螺母生產的A型螺絲,同樣也可以在B型螺母上直接使用按着B型螺母標准生產的B型螺絲。但是由於A型螺母和B型螺母的標准不一樣,用戶在A型螺母上不能直接使用B型的螺絲,反之也一樣。該如何達到這個目的呢?
使用適配器就可以解決這個問題:生產一種“A型螺母適配器”,這種A型螺母適配器的前端符合A型螺母標准要求,可以擰在A型螺母上,后端又焊接了一個B型螺母。這樣用戶就可以借助A型螺母適配器在A型螺母上使用B型的螺絲了。
適配器模式又稱為包裝器,是用來將一個類的接口轉換成客戶希望的另外一個接口。這可以使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。適配器模式的關鍵是建立一個適配器,這個適配器實現了目標接口並且包含了被適配者的引用。
適配器模式的三種角色:
一、目標:目標是一個接口,該接口是客戶想要使用的接口。
二、被適配者:被適配者是一個已經存在的接口或抽象類,這個接口接口或者抽象類需要適配。
三、適配器:適配器是一個類,該類實現了目標接口並且包含有被適配者的引用,即適配器的職責是對適配者接口或抽象類與目標接口進行適配。
以下通過一個簡單的問題來描述適配器模式中所涉及的各個角色。
用戶已經有一個兩廂的插座,但是最近用戶又有了一個新的三廂插座。用戶現有一台洗衣機和一台電視機,洗衣機是三廂插頭,而電視機是兩廂插頭。現在用戶想用心的三廂插座來使用洗衣機和電視機,即用心的三廂插座為洗衣機和電視機接通電流。
針對以上問題,使用適配器模式設計若干個類。
1.目標
本問題是使用三廂插座來為電視機和洗衣機接通電流,所以目標是三廂插座。把三廂插座設置為一個接口:
1 package com.adatpe; 2 3 //適配目標:三相插座 4 public interface ThreeElectricOutlet { 5 void connectElectricCurrent(); 6 }
2.被適配者
對於本問題,用戶是想要用三廂插座為兩廂插頭的電視機接通電流,所以被適配者應該是兩廂插座,也設置為一個接口:
1 package com.adatpe; 2 3 //被適配者:兩相插座 4 public interface TwoElectricOutlet { 5 void connectElectricCurrent(); 6 }
3.適配器
該適配器實現了目標接口三廂插座ThreeElectricOutlet,同時又包含了兩廂插座TwoElectricOutlet的引用:
1 package com.adatpe; 2 3 //適配器:實現目標接口 4 public class ThreeElectricAdapter implements ThreeElectricOutlet { 5 //適配器包含被適配者的引用 6 private TwoElectricOutlet outlet; 7 public ThreeElectricAdapter(TwoElectricOutlet outlet) { 8 this.outlet = outlet; 9 } 10 public void connectElectricCurrent() { 11 outlet.connectElectricCurrent(); 12 } 13 14 }
下列應用程序中,Application.java使用了適配器模式中所涉及的類,應用程序負責用Wash類創建一個對象來模擬一台洗衣機,使用TV類創建一個對象來模擬一台電視機
使用ThreeElectricOutlet接口變量調用Wash對象的connectElectricCurrent()方法,並借助適配器調用TV對象的connectElectricCurrent()方法,即用三廂插座分別為洗衣機和電視機接通電流。
1 package com.adatpe; 2 3 public class Application { 4 public static void main(String[] args) { 5 ThreeElectricOutlet outlet; //目標接口(三相插座) 6 Wash wash = new Wash(); //洗衣機 7 outlet = wash; //洗衣機插在三相插座上 8 System.out.println("使用三相插座接通電流"); 9 outlet.connectElectricCurrent(); //接通電流開始洗衣服 10 TV tv = new TV(); //電視機 11 ThreeElectricAdapter adapter = new ThreeElectricAdapter(tv); //把電視插在適配器上面 12 outlet = adapter; //再把適配器插在三廂插座上 13 System.out.println("使用三廂插座接通電流"); 14 outlet.connectElectricCurrent(); //接通電流,開始播放電視節目 15 } 16 } 17 18 //洗衣機使用三相插座 19 class Wash implements ThreeElectricOutlet{ 20 private String name; 21 public Wash() { 22 name = "黃河洗衣機"; 23 } 24 public Wash(String name){ 25 this.name = name; 26 } 27 public void connectElectricCurrent() { 28 turnOn(); 29 } 30 public void turnOn(){ 31 System.out.println(name+"開始洗衣服了"); 32 } 33 } 34 35 36 //電視機使用兩廂插座 37 class TV implements TwoElectricOutlet{ 38 private String name; 39 public TV() { 40 name = "長江電視機"; 41 } 42 public TV(String name){ 43 this.name = name; 44 } 45 public void connectElectricCurrent() { 46 turnOn(); 47 } 48 public void turnOn(){ 49 System.out.println(name+"開始播放電視節目"); 50 } 51 52 }
運行結果為:
使用三相插座接通電流
黃河洗衣機開始洗衣服了
使用三廂插座接通電流
長江電視機開始播放電視節目
下面舉個雙向適配器的例子
在適配器模式中,如果Adapter角色同時實現目標接口和被適配者接口,並包含目標接口和被適配接口的引用,那么該適配器就是一個雙向適配器。使用雙向適配器,用戶既可以用新的接口又可以用已有的接口。在以上例子中,如果用戶希望能有三廂插座來接通洗衣機和電視機的電流,有可以用兩廂插座來接通洗衣機和電視機的電流,那么就必須使用一個雙向適配器。具體代碼如下:
1 package com.adatpe; 2 3 public class ThreeAndTwoElectricAdapter implements ThreeElectricOutlet, 4 TwoElectricOutlet { 5 private ThreeElectricOutlet threeElectricOutlet; 6 private TwoElectricOutlet twoElectricOutlet; 7 public ThreeAndTwoElectricAdapter(ThreeElectricOutlet threeOutlet,TwoElectricOutlet twoOutlet) { 8 threeElectricOutlet = threeOutlet; 9 twoElectricOutlet = twoOutlet; 10 } 11 public ThreeAndTwoElectricAdapter(TwoElectricOutlet twoOutlet,ThreeElectricOutlet threeOutlet){ 12 threeElectricOutlet = threeOutlet; 13 twoElectricOutlet = twoOutlet; 14 } 15 public void connectElectricCurrent() { 16 if(this instanceof ThreeElectricOutlet){ 17 twoElectricOutlet.connectElectricCurrent();//twoElectricOutlet是被適配的接口 18 } 19 if(this instanceof TwoElectricOutlet){ 20 threeElectricOutlet.connectElectricCurrent(); //threeElectricOutlet是被適配的接口 21 } 22 } 23 public static void main(String[] args) { 24 ThreeElectricOutlet threeOutlet; 25 TwoElectricOutlet twOutlet; 26 Wash wash = new Wash(); 27 TV tv = new TV(); 28 ThreeAndTwoElectricAdapter adapter = new ThreeAndTwoElectricAdapter(wash,tv); 29 threeOutlet = adapter; 30 System.out.println("使用三廂插座接通電源"); 31 threeOutlet.connectElectricCurrent(); 32 twOutlet = adapter; 33 System.out.println("使用兩廂插座接通電源"); 34 twOutlet.connectElectricCurrent(); 35 } 36 37 }
運行結果為:
使用三廂插座接通電源
長江電視機開始播放電視節目
黃河洗衣機開始洗衣服了
使用兩廂插座接通電源
長江電視機開始播放電視節目
黃河洗衣機開始洗衣服了
這樣就實現了即可以用三廂插座又可以用兩廂插座來為電視機和洗衣機接通電流了。
使用適配器模式主要有以下優點:
1.目標和被適配者是完全解耦的關系。
2.適配器模式滿足“開--閉原則”,當添加一個實現了Adapter接口的新類時,不必修改Adapter,Adapter就能對這個新類的實例進行適配。