第17章:適配器模式
適配器模式
適配器模式(adapter):將一個類的接口轉換成客戶希望的另外一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
在軟件開發中,系統的數據和行爲都正確,但接口不符時,應該考慮用適配器,目的是使控制範圍之外的一個原有對象與某個接口匹配。適配器模式
主要應用於希望複用一些現存的類,但是接口又與複用環境要求不一致的情況。
Target
:客戶期待的接口,目標可以是具體類或抽象類,也可以是接口。
Adaptee
:需要適配的類。
Adapter
:通過在內部包裝一個Adaptee
對象,把源接口轉換成目標接口。
客戶端代碼如下:
GoF
《設計模式》講解了兩種適配器類型:類適配器模式
和對象適配器模式
。
類適配器模式
:通過多重繼承對一個接口與另一個接口進行匹配。
對象適配器模式
:將現有組件庫中已經實現的組件引入適配器類中,該類同時實現當前系統的業務接口。
適配器模式的使用
在使用一個已經存在的類時,如果它的接口(方法)和使用要求不相同時,即兩個類所做的事情相同或相似,但是具有不同的接口時,應該考慮用適配器模式。
由於類都共享同一個接口,使得客戶代碼可以統一調用同一接口,使代碼可以更簡單、更直接、更緊湊。
使用適配器模式屬於無奈之舉(亡羊補牢),通常在軟件開發後期或維護期,當雙方都不太容易修改的時候再考慮使用適配器模式適配。在設計階段,類和方法的命名應有規範,若接口不相同,首先應考慮通過重構統一接口,而非適配器。若設計子系統時考慮使用第三方開發組件,而該組件接口與自己的系統
接口不相同,則完全沒有必要爲了迎合該組件而改動自己的接口,此時儘管是在開發設計階段,也可以考慮用適配器模式來解決接口不同的問題。
注意事項
如果能事先預防接口不同問題,不匹配問題就不會發生;當小的接口不統一問題發生時,應及時重構,防止問題擴大;只有碰到無法改變原有設計和代碼的情況時,才考慮適配。事後控制不如事中控制,事中控制不如事前控制。
適配器模式的.NET
應用
.NET
中已經實現的適配器DataAdapter
,用於DataSet
和數據源之間的適配器以便檢索和保存數據。DataAdapter
通過映射Fill
(更改DataSet
中的數據以便與數據源中的數據相匹配)和Update
(更改數據源中的數據以便與DataSet
中的數據相匹配)提供適配器。
適配器模式示例
任務:籃球翻譯
from abc import ABC, abstractmethod
from typing import Text
class Player(ABC):
"""
球員
"""
def __init__(self, name: Text) -> None:
self._name = name
@abstractmethod
def attack(self) -> None:
pass
@abstractmethod
def defense(self) -> None:
pass
class Forwards(Player):
"""
前鋒
"""
def __init__(self, name: Text) -> None:
super(Forwards, self).__init__(name)
def attack(self) -> None:
print("前鋒%s進攻" % self._name)
def defense(self) -> None:
print("前鋒%s防守" % self._name)
class Center(Player):
"""
中鋒
"""
def __init__(self, name: Text) -> None:
super(Center, self).__init__(name)
def attack(self) -> None:
print("中鋒%s進攻" % self._name)
def defense(self) -> None:
print("中鋒%s防守" % self._name)
class Guards(Player):
"""
後衛
"""
def __init__(self, name: Text) -> None:
super(Guards, self).__init__(name)
def attack(self) -> None:
print("後衛%s進攻" % self._name)
def defense(self) -> None:
print("後衛%s防守" % self._name)
class ForeignCenter:
"""
中鋒
"""
def __init__(self) -> None:
self._name = None
@property
def name(self) -> Text:
return self._name
@name.setter
def name(self, name: Text) -> None:
self._name = name
def 進攻(self) -> None:
print("中鋒%s進攻" % self._name)
def 防守(self) -> None:
print("中鋒%s防守" % self._name)
class Translator(Player):
"""
翻譯者
"""
def __init__(self, name: Text) -> None:
super(Translator, self).__init__(name)
self._foreigner = ForeignCenter()
self._foreigner.name = name
def attack(self) -> None:
self._foreigner.進攻()
def defense(self) -> None:
self._foreigner.防守()
# 客戶端代碼
if __name__ == "__main__":
player_b = Forwards("巴蒂爾")
player_b.attack()
player_m = Guards("麥克格雷迪")
player_m.defense()
player_y = Translator("姚明")
player_y.attack()
player_y.defense()
前鋒巴蒂爾進攻
後衛麥克格雷迪防守
中鋒姚明進攻
中鋒姚明防守