從零開始學設計模式(六)——介面卡模式(Adapter Pattern)
介面卡模式
此模式難度係數為 初級 ,由 Gang Of Four 提出。
介面卡模式是作為兩個不相容的介面之間的橋樑,這種型別的設計模式屬於結構型模式,它結合了兩個獨立介面的功能。
這種模式涉及到一個單一的類,該類負責加入獨立的或不相容的介面功能。
此模式一般應用於已經存在的類介面中,有動機地修改一個正常執行的系統的介面,這時應該考慮使用介面卡模式。
意圖
將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
主要解決:主要解決在軟體系統中,常常要將一些"現存的物件"放到新的環境中,而新環境要求的介面是現物件不能滿足的。
何時使用:
1、系統需要使用現有的類,而此類的介面不符合系統的需要。
2、想要建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作,這些源類不一定有一致的介面。
3、通過介面轉換,將一個類插入另一個類系中。(比如老虎和飛禽,現在多了一個飛虎,在不增加實體的需求下,增加一個介面卡,在裡面包容一個虎物件,實現飛的介面。)
如何解決:繼承或依賴(推薦)。
關鍵程式碼:介面卡繼承或依賴已有的物件,實現想要的目標介面
解釋
現實世界中的例子
讀卡器作為記憶體SD卡和電腦之間的介面卡,你將記憶體SD卡插入讀卡器,再將讀卡器插入電腦USB介面,這樣電腦可以通過讀卡器來讀取SD卡中的資料
另一個例子是著名的電源介面卡;三腳插頭不能連線到雙管插座,它需要使用一個電源介面卡,使其與雙管插座相容。還有一個例子是翻譯人員將一個人所說的話翻譯成另一個人所說的話
簡而言之
介面卡模式允許你在介面卡中包裝一個不相容的物件,使其與另一個類相容
維基百科
>In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code(在軟體工程中,介面卡模式是一種軟體設計模式,允許現有類的介面用作另一個介面。它通常用於使現有類與其他類一起工作,而不修改它們的原始碼)
程式示例
考慮一個只能使用划艇而根本不能航行的船長。
首先我們有一個介面划艇RowingBoat和漁船FishingBoat
/** * The interface expected by the client.<br> * A rowing boat is rowed to move. * */ public interface RowingBoat { void row(); } /** * * Device class (adaptee in the pattern). We want to reuse this class. * Fishing boat moves by sailing. * */ public class FishingBoat { private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class); public void sail() { LOGGER.info("The fishing boat is sailing"); } }
接下來定義Captain船長類,船長Uses RowingBoat
/** * The Captain uses {@link RowingBoat} to sail. <br> * This is the client in the pattern. */ public class Captain { private RowingBoat rowingBoat; public Captain() {} public Captain(RowingBoat rowingBoat) { this.rowingBoat = rowingBoat; } public void setRowingBoat(RowingBoat rowingBoat) { this.rowingBoat = rowingBoat; } public void row() { rowingBoat.row(); } }
現在,假設海盜來了,我們的船長需要逃跑,但是隻有漁船可用。我們需要創造一個介面卡,讓船長能夠用他的划艇技能操作漁船
/** * * Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link RowingBoat} * interface expected by the client ({@link Captain}). * */ public class FishingBoatAdapter implements RowingBoat { private FishingBoat boat; public FishingBoatAdapter() { boat = new FishingBoat(); } @Override public void row() { boat.sail(); } }
最後”船長“使用漁船逃脫了海盜的追擊
public class App { /** * Program entry point. * * @param args command line args */ public static void main(String[] args) { // The captain can only operate rowing boats but with adapter he is able to use fishing boats as well Captain captain = new Captain(new FishingBoatAdapter()); captain.row(); } }
結果
被適配的物件和類介面卡有不同的結果取捨。
類介面卡:
- 通過提交一個具體的介面卡類使適配物件適應目標。因此,當我們想要調整一個適配物件類及其所有子類時,類介面卡將不起作用
- 只引入一個物件,不需要額外的指標間接指向適配物件。
- 讓介面卡重寫適配物件的一些方法,因為介面卡是適配物件的子類。
適配物件:
- 允許我們用一個介面卡來處理許多適配物件,即適配物件本身及其所有子類(如果有的話)。介面卡還可以同時向所有適配物件新增功能。
- 使得更難重寫適配物件的方法。這將需要對所有的適配物件進行子類化,並使介面卡引用適配物件子類而不是適配物件本身。
Java中的現例項子
- ofollow,noindex">java.util.Arrays#asList()
- java.util.Collections#list()
- java.util.Collections#enumeration()
- javax.xml.bind.annotation.adapters.XMLAdapter
寫在最後
介面卡物件是我們接觸到的第一個結構型模式,需要注意的是介面卡模式一般不是在新的程式設計時使用和新增的,而是在解決正在服役的專案內外介面相容性問題的。
說實話,我們應該熟練掌握這種設計模式,因為大多數時候我們是來解決現役專案問題的。
因此接下來我們再來編寫一個介面卡模式小程式,就以上面經典的電腦讀取記憶體SD卡的資料為例。
首先電腦通過USB介面可以讀取讀卡器中的資料,而讀卡器可以讀取SD記憶體卡中的資料。按此思路畫出程式UML類圖如下:

diagram-adapter.png
根據類圖我們開始編寫程式,步驟一定義電腦擴充套件介面ComputerExtendInterface和MicroSD快閃記憶體卡類:
public interface ComputerExtendInterface { void usbReadData(); } public class MicroSD { private static Logger loggger = LoggerFactory.getLogger(MicroSD.class); public void readSD(){ loggger.info("reading data from Micro SD...."); } }
步驟二定義介面卡類讀卡器:
/** * 適配MicroSD 到ComputerExtendInterface 介面 */ public class ReadCardAdapter implements ComputerExtendInterface{ private MicroSD microSD; public ReadCardAdapter(){ this.microSD = new MicroSD(); } @Override public void usbReadData() { microSD.readSD(); } }
步驟三定義適配客戶端電腦類:
public class Computer { private ComputerExtendInterface extendInterface; publicComputer(ReadCardAdapter readCardAdapter){ this.extendInterface = readCardAdapter; } /** * 讀取資料 */ public void readData(){ extendInterface.usbReadData(); } }
步驟四完成App的呼叫編寫:
public class App { public static void main(String[] args) { Computer computer = new Computer(new ReadCardAdapter()); computer.readData(); } }
執行App輸出結果如下:
19:39:48.552 [main] INFO com.lyp.adapter.MicroSD - reading data from Micro SD....
下一篇文章我們將學習結構性模式中的 橋接模式(Bridge Pattern)
碼字不易,各位看官如果喜歡的話,請給點個喜歡 :heart:,關注下我,我將努力持續不斷的為大家更新