1. 程式人生 > >一個示例讓你明白介面卡模式

一個示例讓你明白介面卡模式

一個示例讓你明白介面卡模式

現實生活中的介面卡

 

本文討論介面卡模式。介面卡模式是23中設計模式之一,它的主要作用是在新介面和老介面之間進行適配。它非常像我們出國旅行時帶的電源轉換器。為了舉這個例子,我還特意去京東上搜了一下電源轉換器,確實看到了很多地方的標準不一樣。我們國家的電器使用普通的扁平兩項或三項插頭,而去外國的話,使用的標準就不一樣了,比如德國,使用的是德國標準,是兩項圓頭的插頭。如果去德國旅遊,那麼我們使用的手機充電器插頭無法插到德國的插排中去,那就意味著我們無法給手機充電。怎樣解決這個問題呢?只要使用一個電源轉化器就行了。如下圖所示:

 

該介面卡下面的插頭符合德國標準,可以插到德國的插排中去,上面提供的介面符合國標,可以供我們的手機充電器使用。

 

 

 

實現電源介面卡

 

下面我們使用程式碼來表述介面卡模式:

 

程式碼中有兩個介面,分別為德標介面和國標介面,分別命名為DBSocketInterface和GBSocketInterface,此外還有兩個實現類,分別為德國插座和中國插座,分別為DBSocket和GBSocket。為了提供兩套介面之間的適配,我們提供了一個介面卡,叫做SocketAdapter。除此之外,還有一個客戶端,比如是我們去德國旅遊時住的一家賓館,叫Hotel,在這個德國旅館中使用德國介面。

 

德標介面:

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. /** 
  2.  * 德標介面 
  3.  */  
  4. public interface DBSocketInterface {  
  5.       
  6.     /** 
  7.      * 這個方法的名字叫做:使用兩項圓頭的插口供電 
  8.      * 本人英語就這個水平 
  9.      */  
  10.     void powerWithTwoRound();  
  11. }  


德國插座實現德標介面

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. /** 
  2.  * 德國插座 
  3.  */  
  4. public class DBSocket implements DBSocketInterface{  
  5.       
  6.     public void powerWithTwoRound(){  
  7.         System.out.println("使用兩項圓頭的插孔供電");  
  8.     }  
  9. }  


德國旅館是一個客戶端,它裡面有德標的介面,可以使用這個德標介面給手機充電:

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. /** 
  2.  * 德國賓館 
  3.  */  
  4. public class Hotel {  
  5.   
  6.     //旅館中有一個德標的插口  
  7.     private DBSocketInterface dbSocket;  
  8.       
  9.     public Hotel(){}  
  10.       
  11.     public Hotel(DBSocketInterface dbSocket) {  
  12.         this.dbSocket = dbSocket;  
  13.     }  
  14.   
  15.     public void setSocket (DBSocketInterface dbSocket){  
  16.         this.dbSocket = dbSocket;  
  17.     }  
  18.   
  19.     //旅館中有一個充電的功能  
  20.     public void charge(){  
  21.           
  22.         //使用德標插口充電  
  23.         dbSocket.powerWithTwoRound();  
  24.     }  
  25. }  


 

 

現在寫一段程式碼進行測試:

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         //初始化一個德國插座物件, 用一個德標介面引用它  
  6.         DBSocketInterface dbSoket = new DBSocket();  
  7.           
  8.         //建立一個旅館物件  
  9.         Hotel hotel = new Hotel(dbSoket);  
  10.           
  11.         //在旅館中給手機充電  
  12.         hotel.charge();  
  13.     }  
  14. }  

 

執行程式,打印出以下結果: 使用兩項圓頭的插孔供電

 

現在我去德國旅遊,帶去的三項扁頭的手機充電器。如果沒有帶電源介面卡,我是不能充電的,因為不可能為了我一個旅客而為我更改牆上的插座,更不可能為我專門蓋一座使用中國國標插座的賓館。因為人家德國人一直這麼使用,並且用的挺好,俗話說入鄉隨俗,我就要自己想辦法來解決問題。對應到我們的程式碼中,也就是說,上面的Hotel類,DBSocket類,DBSocketInterface介面都是不可變的(由德國的客戶提供),如果我想使用這一套API,那麼只能自己寫程式碼解決。

 

下面是國標介面和中國插座的程式碼。


國標介面:

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. /** 
  2.  * 國標介面 
  3.  */  
  4. public interface GBSocketInterface {  
  5.       
  6.     /** 
  7.      * 這個方法的名字叫做:使用三項扁頭的插口供電 
  8.      * 本人英語就這個水平,從有道詞典查得, flat意思好像是: 扁的 
  9.      */  
  10.     void powerWithThreeFlat();  
  11. }  


中國插座實現國標介面:

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. /** 
  2.  * 中國插座 
  3.  */  
  4. public class GBSocket implements GBSocketInterface{  
  5.       
  6.     @Override  
  7.     public void powerWithThreeFlat() {  
  8.         System.out.println("使用三項扁頭插孔供電");  
  9.     }  
  10. }  


可以認為這兩個東西是我帶到德國去的,目前他們還不能使用,因為介面不一樣。那麼我必須建立一個介面卡,這個介面卡必須滿足以下條件:

 

1    必須符合德國標準的介面,否則的話還是沒辦法插到德國插座中;

2    在呼叫上面實現的德標介面進行充電時,提供一種機制,將這個呼叫轉到對國標介面的呼叫 。

 

這就要求:

1 介面卡必須實現原有的舊的介面

2 介面卡物件中持有對新介面的引用,當呼叫舊介面時,將這個呼叫委託給實現新介面的物件來處理,也就是在介面卡物件中組合一個新介面。

 

 

下面給出介面卡類的實現:

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. public class SocketAdapter    
  2.         implements DBSocketInterface{   //實現舊介面  
  3.   
  4.     //組合新介面  
  5.     private GBSocketInterface gbSocket;  
  6.       
  7.     /** 
  8.      * 在建立介面卡物件時,必須傳入一個新街口的實現類 
  9.      * @param gbSocket 
  10.      */  
  11.     public SocketAdapter(GBSocketInterface gbSocket) {  
  12.         this.gbSocket = gbSocket;  
  13.     }  
  14.   
  15.       
  16.     /** 
  17.      * 將對就介面的呼叫適配到新介面 
  18.      */  
  19.     @Override  
  20.     public void powerWithTwoRound() {  
  21.           
  22.         gbSocket.powerWithThreeFlat();  
  23.     }  
  24.   
  25. }  


這個介面卡類滿足了上面的兩個要求。下面寫一段測試程式碼來驗證一下介面卡能不能工作,我們按步驟一步步的寫出程式碼,以清楚的說明介面卡是如何使用的。

 

1    我去德國旅遊,帶去的充電器是國標的(可以將這裡的GBSocket看成是充電器)

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. GBSocketInterface gbSocket = new GBSocket();  


2    來到德國後, 找到一家德國賓館住下 (這個賓館還是上面程式碼中的賓館,使用的依然是德國標準的插口)

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. Hotel hotel = new Hotel();  


3    由於沒法充電,我拿出隨身帶去的介面卡,並且將我帶來的充電器插在介面卡的上端插孔中。這個上端插孔是符合國標的,我的充電器完全可以插進去。

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. SocketAdapter socketAdapter = new SocketAdapter(gbSocket);  


4     再將介面卡的下端插入賓館裡的插座上

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. hotel.setSocket(socketAdapter);  


5    可以在賓館中使用介面卡進行充電了

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. hotel.charge();  


 

上面的五個步驟就是介面卡的使用過程,下面是完整的測試程式碼。

 

[java] view plaincopy在CODE上檢視程式碼片派生到我的程式碼片

  1. public class TestAdapter {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         GBSocketInterface gbSocket = new GBSocket();  
  6.           
  7.         Hotel hotel = new Hotel();  
  8.           
  9.         SocketAdapter socketAdapter = new SocketAdapter(gbSocket);  
  10.           
  11.         hotel.setSocket(socketAdapter);  
  12.           
  13.         hotel.charge();  
  14.     }  
  15. }  


執行上面的程式,打印出以下結果:

使用三項扁頭插孔供電

 

這說明介面卡起作用了,上一個例項中列印的是:使用兩項圓頭的插孔供電。 現在可以使用三項扁頭插孔供電了。我們並沒有改變賓館中的德標插口,提供了一個介面卡就能使用國標的插口充電。這就是介面卡模式的魅力:不改變原有介面,卻還能使用新介面的功能。

 

由於上面的程式碼都是分片的,沒有完整的專案原始碼,為了使讀者對示例中的類和介面更清晰,下面給出UML類圖:

 

 

 

總結

 

根據上面的示例,想必讀者應該能比較深入的瞭解到了介面卡模式的魔力。下面給出介面卡模式的定義(該定義來自於《Head First 設計模式》):

 

介面卡模式將一個類的介面轉換成客戶期望的另一個介面,讓原本不相容的介面可以合作無間。

 

下面給出介面卡模式的類圖(該類圖同樣來自於《Head First 設計模式》):

 

 

 

介面卡模式的三個特點:

 

1    介面卡物件實現原有介面

2    介面卡物件組合一個實現新介面的物件(這個物件也可以不實現一個介面,只是一個單純的物件)

3    對介面卡原有介面方法的呼叫被委託給新介面的例項的特定方法

 

 

有人認為講解設計模式的例子都太簡單,看著感覺是那麼回事,但是要是真想在專案開發中使用,還真是應用不到。其實我們不必在專案中刻意使用設計模式,而是應該從實際的設計問題出發,看哪個模式能解決我們的問題,就使用哪個模式。不要為了使用模式而使用模式,那樣就捨本逐末了,一般情況下,只要遵循一定的設計原則就可以了,設計模式也是根據這些原則被總結出來的,熟悉了這些原則,模式自然而然就有了。

 

其實只要平時善於思考了感悟,在專案中是可以用到設計模式的,並且如果用的合理的話,會為此而受益良多。在下一篇部落格中,我會以專案中遇到的一個真實的需求來解析介面卡模式的使用,敬請期待。

 

 

 

 

關於上面提到的介面卡模式實際應用的文章已經完成並發表, 感興趣的朋友可以看一下, 如果有不合理的地方還請指正。

文章連結 運用介面卡模式應對專案中的變化