問題
:假設有一個軟件系統,你希望它能在不改變現有代碼的前提下和一個新的廠商類庫搭配使用,但是這個新廠商所設計出來的接口不同於舊廠商的接口
這個問題和下圖的問題類似
美國標準的插頭🔌無法在歐洲標準的插座上使用,通常的做法是什麼呢?
添加一個插頭適配器,適配器的作用是將歐式插頭轉換成美式插座,以便於讓美式插頭可以使用。
解決方案
所以,面對一個有全新接口的類庫而又不能改變現有代碼時,最先想到的做法是,在這兩個系統之間添加一個適配器。
簡單的例子
有一個系統,需要一個鴨子🦆對象,但是現在只有一個火雞🦃對象。鴨子和火雞對象的功能簡單描述如下:
# 鴨子的簡單描述
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 設計模式》。
最後,感謝女朋友支持和包容,比❤️
也可以在公號輸入以下關鍵字獲取歷史文章:公號&小程序
| 設計模式
| 併發&協程