适配器模式是将目标对象的接口转换为使用者所期望的另外一个接口,使得原本接口类型不一致的类能够更好的工作在一起。
使用场景:
两个类所做的事情相同或相似,但是具有不同的接口时要使用它。客户端可以统一调用同一接口,这样应该可以更简单、更直接、更紧凑。
一.需求提出
假设现在要求写一个程序,这个程序示意一个足球对的训练过程。这个足球队有着中方球员、外援。由于语言文化不同。中方球员和外籍球员的控制接口是不一致的。
二.无设计的实现
`
# -*- coding: utf-8 -*-
class chinese_player:
def she_men(self):
print('射门!!!')
def fang_shou(self):
print('铲球!!!')
class foreign_player:
def shooting(self):
print('shoot Door!!!')
def defense(self):
print('Tackle!!!')
class coach:
def __init__(self, *team_plays):
self._players = team_plays
def trainning(self):
for player in self._players:
if isinstance(player, foreign_player):
player.shooting()
player.defense()
elif isinstance(player, chinese_player):
player.she_men()
player.fang_shou()
# 如果有更多的不同接口类型的球员,则需要写更多的分支
if __name__ == '__main__' :
# 客户端
wulei = chinese_player()
elkeson = foreign_player()
lipe = coach(wulei,elkeson)
lipe.trainning()
`
主要的问题:
1> 当程序需要扩展,需要处理更多类型的球员时,Coach 类的 trainning 函数需要不断的去加分支。违反了开放封闭原则。
2> trainning中的isinstance接口,给人一个明显的信号就是要识别具体的对象类型,根据具体的类型再做具体的操作。明显的是依赖具体实现进行编程。违背了依赖倒置原则
附:
依赖倒置原则DIP(Dependency Inversion Principle):
高层模块不应该依赖于低层模块;二者都应该依赖于抽象。
抽象不应该依赖于细节;细节应该依赖抽象。
要针对接口编程,而不是针对实现编程。
单一职责原则SRP(Single Responsibility Principle):
就一个逻辑单元(类、函数)而言,应该仅有一个引起变化的原因。分离关注点;正交设计;
三.适配器模式重构
`
class chinese_player:
def she_men(self):
print('射门!!!')
def fang_shou(self):
print('铲球!!!')
class foreign_player:
def shooting(self):
print('shoot Door!!!')
def defense(self):
print('Tackle!!!')
class translater:
def __init__(self):
self._player = chinese_player()
def shooting(self):
self._player.she_men()
def defense(self):
self._player.fang_shou()
class coach:
def __init__(self, *team_plays):
self._players = team_plays
def trainning(self):
# 无需再对player的类型做具体的判断和处理,针对shooting defense通用接口变成。
for player in self._players:
player.shooting()
player.defense()
if __name__ == '__main__':
# 为中国球员请个洋翻译:)
wulei = translater()
elkeson = foreign_player()
lipe = coach(wulei, elkeson)
lipe.trainning()
`
分析
1> translater类作为一个适配器,实现了 she_men ->shooting fang_shou->defense 函数接口的映射,让shooting 和 defense变为通用函数接口。
2> 有了适配器的函数接口转换,coach核心类的 trainning 接口,无需再做球员类型的具体判断,只需要针对通用接口编程即可。后续的功能扩展,也无需再对training接口进行变更。