第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()
前锋巴蒂尔进攻
后卫麦克格雷迪防守
中锋姚明进攻
中锋姚明防守