設計模式之適配器模式

現實生活中的適配器


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


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



實現電源適配器


下面我們使用代碼來表述適配器模式:

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

德標接口:
public interface DBSocketInterface {  
      
    /** 
     * 這個方法的名字叫做:使用兩項圓頭的插口供電 
     * 本人英語就這個水平 
     */  
    void powerWithTwoRound();  
}  


德國插座實現德標接口
    /** 
     * 德國插座 
     */  
    public class DBSocket implements DBSocketInterface{  
          
        public void powerWithTwoRound(){  
            System.out.println("使用兩項圓頭的插孔供電");  
        }  
    }  


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

    /** 
     * 德國賓館 
     */  
    public class Hotel {  
      
        //旅館中有一個德標的插口  
        private DBSocketInterface dbSocket;  
          
        public Hotel(){}  
          
        public Hotel(DBSocketInterface dbSocket) {  
            this.dbSocket = dbSocket;  
        }  
      
        public void setSocket (DBSocketInterface dbSocket){  
            this.dbSocket = dbSocket;  
        }  
      
        //旅館中有一個充電的功能  
        public void charge(){  
              
            //使用德標插口充電  
            dbSocket.powerWithTwoRound();  
        }  
    }  


現在寫一段代碼進行測試:
    public class Test {  
      
        public static void main(String[] args) {  
              
            //初始化一個德國插座對象, 用一個德標接口引用它  
            DBSocketInterface dbSoket = new DBSocket();  
              
            //創建一個旅館對象  
            Hotel hotel = new Hotel(dbSoket);  
              
            //在旅館中給手機充電  
            hotel.charge();  
        }  
    }  

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

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

下面是國標接口和中國插座的代碼。

國標接口:
    /** 
     * 國標接口 
     */  
    public interface GBSocketInterface {  
          
        /** 
         * 這個方法的名字叫做:使用三項扁頭的插口供電 
         * 本人英語就這個水平,從有道詞典查得, flat意思好像是: 扁的 
         */  
        void powerWithThreeFlat();  
    }  


中國插座實現國標接口:

    /** 
     * 中國插座 
     */  
    public class GBSocket implements GBSocketInterface{  
          
        @Override  
        public void powerWithThreeFlat() {  
            System.out.println("使用三項扁頭插孔供電");  
        }  
    }  


可以認爲這兩個東西是我帶到德國去的,目前他們還不能使用,因爲接口不一樣。那麼我必須創建一個適配器,這個適配器必須滿足以下條件:

1    必須符合德國標準的接口,否則的話還是沒辦法插到德國插座中;
2    在調用上面實現的德標接口進行充電時,提供一種機制,將這個調用轉到對國標接口的調用 。

這就要求:
1 適配器必須實現原有的舊的接口
2 適配器對象中持有對新接口的引用,當調用舊接口時,將這個調用委託給實現新接口的對象來處理,也就是在適配器對象中組合一個新接口。


下面給出適配器類的實現:
    public class SocketAdapter    
            implements DBSocketInterface{   //實現舊接口  
      
        //組合新接口  
        private GBSocketInterface gbSocket;  
          
        /** 
         * 在創建適配器對象時,必須傳入一個新街口的實現類 
         * @param gbSocket 
         */  
        public SocketAdapter(GBSocketInterface gbSocket) {  
            this.gbSocket = gbSocket;  
        }  
      
          
        /** 
         * 將對就接口的調用適配到新接口 
         */  
        @Override  
        public void powerWithTwoRound() {  
              
            gbSocket.powerWithThreeFlat();  
        }  
      
    }  

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

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

    GBSocketInterface gbSocket = new GBSocket();  

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

    Hotel hotel = new Hotel();  

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

    SocketAdapter socketAdapter = new SocketAdapter(gbSocket);

4     再將適配器的下端插入賓館裏的插座上

    hotel.setSocket(socketAdapter); 

5    可以在賓館中使用適配器進行充電了
    hotel.charge();  

上面的五個步驟就是適配器的使用過程,下面是完整的測試代碼。

    public class TestAdapter {  
      
        public static void main(String[] args) {  
              
            GBSocketInterface gbSocket = new GBSocket();  
              
            Hotel hotel = new Hotel();  
              
            SocketAdapter socketAdapter = new SocketAdapter(gbSocket);  
              
            hotel.setSocket(socketAdapter);  
              
            hotel.charge();  
        }  
    }  


運行上面的程序,打印出以下結果:
使用三項扁頭插孔供電

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

由於上面的代碼都是分片的,沒有完整的項目源碼,爲了使讀者對示例中的類和接口更清晰,下面給出UML類圖:




總結


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

適配器模式將一個類的接口轉換成客戶期望的另一個接口,讓原本不兼容的接口可以合作無間。

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




適配器模式的三個特點:

1    適配器對象實現原有接口
2    適配器對象組合一個實現新接口的對象(這個對象也可以不實現一個接口,只是一個單純的對象)
3    對適配器原有接口方法的調用被委託給新接口的實例的特定方法


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

其實只要平時善於思考了感悟,在項目中是可以用到設計模式的,並且如果用的合理的話,會爲此而受益良多。在下列這篇博客中,我會以項目中遇到的一個真實的需求來解析適配器模式的使用,請看:
http://blog.csdn.net/zhangjg_blog/article/details/18779607

轉載出自於:http://blog.csdn.net/zhangjg_blog/article/details/18735243

我自己關於適配器模式的總結:
在不改變舊的接口情況下,定義一個適配器,必須實現舊的接口,然後在適配器中定義一個新接口實例,在適配器中實現舊的接口方法中,完成新接口的行爲。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章