漫話:如何給女朋友解釋什麼是介面卡模式?
週末窩在家裡面打王者榮耀,女朋友在旁邊玩我的電腦,我嫌她播放的綜藝節目聲音比較大,於是建議她戴耳機。
介面卡模式
Adapter Pattern,通常被翻譯成介面卡模式,有時候也叫做包裝模式(wrapper pattern),是GOF 23種設計模式之一。主要作用是將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
《Design Patterns: Elements of Reusable Object-Oriented Software》(《設計模式》),由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著(Addison-Wesley,1995)。這幾位作者常被稱為"Gang of Four),簡稱GOF。
GOF中將介面卡模式分為類介面卡模式和物件介面卡模式。
物件介面卡模式
在這種介面卡模式中,介面卡容納一個它包裹的類的例項。在這種情況下,介面卡呼叫被包裹物件的物理實體。
類介面卡模式
這種介面卡模式下,介面卡繼承自已實現的類(一般多重繼承)。
二者區別僅在於介面卡角色對於被適配角色的適配是通過繼承還是組合來實現的,由於Java中不支援多繼承,而且類介面卡模式有破壞封裝之嫌,而且我們也提倡多用組合少用繼承。所以本文主要介紹物件介面卡。
介面卡模式用途
我們生活中經常需要用到插口轉換器,比如現在很多手機都只有一個插口,這個口可以直接用來充電和聽音樂。但是前提是我們使用的充電器和耳機的插口要和這個裝置適配的。
目前市面上很多手機的插口都是type-c或者Lightning型號:
但是,我們常用的耳機型號卻是2.5mm和3.5mm的圓形介面:

所以,當我們想要把自己的3.5mm圓形介面的耳機插入Lightning或者type-c介面的時候,就需要一個轉換器:
同理,在軟體系統中,常常要將一些"現存的物件"放到新的環境中,而新環境要求的介面是現物件不能滿足。如以下類似的場景:
1、系統需要使用現有的類,而此類的介面不符合系統的需要。
2、想要建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作,這些源類不一定有一致的介面。
3、通過介面轉換,將一個類插入另一個類系中。(比如老虎和飛禽,現在多了一個飛虎,在不增加實體的需求下,增加一個介面卡,在裡面包容一個虎物件,實現飛的介面。)
介面卡模式,就可以解決以上的問題。
介面卡模式實現方式
下面我們就使用介面卡模式,模擬一種場景:使用一個安卓的type-c充電器給只支援 Lightning介面的蘋果手機充電(假設可以完美支援)。
已知,我們有一個type-c充電器、一個Lightning插口的蘋果手機。無論是type-c還是Lightning,都是一種標準,在程式碼中,標準即介面。所以我們先定義兩個介面:
/** * Lightning充電介面 */public interface LightningInterface { public void chargeWithLightning();}/** * TypeC充電介面 */public interface TypeCInterface { public void chargeWithTypeC();}
接下來定義我們的蘋果手機,他只支援使用 Lightning插口充電:
public class IphoneX { private LightningInterface lightningInterface; public IphoneX() { } public IphoneX(LightningInterface lightningInterface) { this.lightningInterface = lightningInterface; } public void charge() { System.out.println("開始給我的IphoneX手機充電..."); lightningInterface.chargeWithLightning(); System.out.println("結束給我的IphoneX手機充電..."); } //setter/getter}
然後再來看看我們的安卓充電器應該如何定義:
/** * 安卓裝置的充電器 */public class AndroidCharger implements TypeCInterface { @Override public void chargeWithTypeC() { System.out.println("使用Type-C型號的充電器充電..."); }}
有了安卓充電器和蘋果手機。接下來,我們就要定義一個介面卡了,希望通過這個介面卡,我們可以實現使用安卓裝置的充電器給蘋果手機充電:
public class Adapter implements LightningInterface { private TypeCInterface typeCInterface; public Adapter() { } public Adapter(TypeCInterface typeCInterface) { typeCInterface = typeCInterface; } @Override public void chargeWithLightning() { typeCInterface.chargeWithTypeC(); } //setter/getter}
這個介面卡實現了LightningInterface,並組合了TypeCInterface,當外部呼叫chargeWithLightning方法的時候,實際上呼叫的是typeCInterface.chargeWithTypeC方法。
就像電源介面卡,他實現的是一個Lightning的規範,自身是一個Lightning的插頭,但實際充電的時候,他是通過typc-c的電源進行的,他起到的是一箇中間轉換的作用。
接著我們定義客戶端,實現我們想要的充電功能:
public class Main { public static void main(String[] args) { Adapter adapter = new Adapter(new AndroidCharger()); IphoneX iphoneX = new IphoneX(); iphoneX.setLightningInterface(adapter); iphoneX.charge(); }}
輸出結果如下:
開始給我的IphoneX手機充電...使用Type-C型號的充電器充電...結束給我的IphoneX手機充電...
上面的例子通過介面卡,我們使用一個安卓的type-c充電器給一個只支援Lightning介面的蘋果手機充電。
上面的程式碼,就是一個介面卡模式的例子,這個例子中,共出現了四種角色:

以上四個角色中,目標抽象類(Lightning介面)、適配者類(安卓充電器)、客戶端(蘋果手機)都是原來程式碼中就有的,我們完全不需要對他們進行修改。只需要引入一個介面卡(介面轉換器)即可。
優缺點
優點
介面卡模式(物件介面卡模式),是一種組合優於整合的思想的實現。通過使用介面卡模式,我們可以最大程度的複用已有的了類和程式碼。他主要有以下有點:
-
將目標類和適配者類解耦,通過引入一個介面卡類來重用現有的適配者類,而無須修改原有程式碼。
-
增加了類的透明性和複用性,將具體的實現封裝在適配者類中,對於客戶端類來說是透明的,而且提高了適配者的複用性。
-
靈活性和擴充套件性都非常好,通過使用配置檔案,可以很方便地更換介面卡,也可以在不修改原有程式碼的基礎上增加新的介面卡類,完全符合“開閉原則”。
缺點
當然,介面卡模式並不是完美的,過度使用還是會帶來一些問題的。缺點如下:
-
過多地使用介面卡,會讓系統非常零亂,不易整體進行把握。比如,明明看到呼叫的是 A 介面,其實內部被適配成了 B 介面的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用介面卡,而是直接對系統進行重構。
使用場景
關於介面卡模式的使用場景,一般主要是當我們需要修改一些正在執行著的程式碼,並且希望可以複用原有程式碼實現新的功能的時候,就要考慮介面卡模式。
在Spring框架中,就大量的使用了介面卡模式,讀者可以開啟自己的IDE,嘗試著以關鍵字"Adapter"全域性搜尋下,一定會有很多的實際應用。
當你遇到的問題,和你想用安卓充電器給蘋果手機充電類似的時候,就一定要想到介面卡模式哦!
這是關於設計模式的第三篇,前兩篇分別是:《漫話:如何給女朋友解釋什麼是策略模式?》《漫話:如何給女朋友解釋什麼是單例模式?》還想學習哪種設計模式,歡迎留言哦。