問題
:假設有一個軟體系統,你希望它能在不改變現有程式碼的前提下和一個新的廠商類庫搭配使用,但是這個新廠商所設計出來的介面不同於舊廠商的介面
這個問題和下圖的問題類似
美國標準的插頭?無法在歐洲標準的插座上使用,通常的做法是什麼呢?
新增一個插頭介面卡,介面卡的作用是將歐式插頭轉換成美式插座,以便於讓美式插頭可以使用。
解決方案
所以,面對一個有全新介面的類庫而又不能改變現有程式碼時,最先想到的做法是,在這兩個系統之間新增一個介面卡。
簡單的例子
有一個系統,需要一個鴨子?物件,但是現在只有一個火雞?物件。鴨子和火雞物件的功能簡單描述如下:
# 鴨子的簡單描述
class Duck:
def quack(self):
# 會呱呱叫
print("Quack")
def fly(self):
# 飛的能力
print("I'm flying")
# 火雞的簡單描述
class Turkey:
def gobble(self):
# 不會呱呱叫,只會咯咯叫
print("Gobble gobble")
def fly(self):
# 飛的能力 但是飛不遠
print("I'm flying a short distance")
複製程式碼
因為現在沒有鴨子物件,只能那火雞物件冒充。由於鴨子物件和火雞物件功能不同,不能直接拿來用,現在就需要使用介面卡來完成這個功能:
class TurkeyAdapter(Duck):
turkey = Turkey() # 這裡實際使用的是火雞物件
# 實現鴨子物件擁有的quack方法
def quack(self):
self.turkey.gobble()
def fly(self):
# 假設火雞比鴨子飛的短,為了模擬鴨子的動作,多飛幾次
for i in range(5):
turkey.fly()
複製程式碼
接下來呼叫就可以像使用鴨子物件一樣使用火雞適配後的物件。
# test
duck = Duck()
duck.quack()
duck.fly()
turkey_adapter = Duck()
turkey_adapter.quack()
turkey_adapter.fly()
複製程式碼
現在再來看一下介面卡使用的過程:
- 客戶通過被適配者實現的介面呼叫介面卡
- 介面卡將請求轉換為被適配者可以響應的請求
- 被適配者響應,把結果返回給介面卡,然後介面卡再將結果響應給客戶。
通過這個例子,接下來看一下介面卡模式的正式定義
定義
介面卡模式:
將一個類的介面,轉換成客戶期望的另一個介面。介面卡讓原本介面不相容的類可以合作。
優點
- 可以通過建立介面卡進行介面轉換,讓不相容的介面相容,讓客戶從實現的介面的解耦。
- 使用物件組合,以修改的介面包裝被適配者
- 被適配的子類可以搭配著介面卡使用
- 滿足開放/封閉原則(open/close principle)
開放/封閉原則
是物件導向設計的基本原則之一,宣告一個軟體實體應該對擴充套件是開放的,對修改是關閉的。
真實世界中的介面卡
- xmltodict 可以將 xml 轉換為 json
- grpc 也可以認為是一種介面卡,提供了跨語言呼叫能力
- sqlalchemy 可以在不改變程式碼的情況下對接多種資料庫
本文例子來自《Head First 設計模式》。
最後,感謝女朋友支援和包容,比❤️
也可以在公號輸入以下關鍵字獲取歷史文章:公號&小程式
| 設計模式
| 併發&協程