python设计模式之适配器模式

结构型设计模式一个系统中不同实体(比如,类和对象)之间的关系,关注的是提供一种简单的对象组合方式来创造功能。

适配器模式( Adapter pattern)是一种结构型设计模式,帮助我们实现两个不兼容接口之间的容。首先,解释一下不兼容接口的真正含义。如果我们希望把一个老组件用于一个新系统中,或者把一个新组件用于一个老系统中,不对代码进行任何修改两者就能够通信的情况很少见。但又并非总是能修改代码,或因为我们无法访问这些代码(例如,组件以外部库的方式提供),或因为修改代码本身就不切实际。在这些情况下,我们可以编写一个额外的代码层,该代码层包含让两个接口之间能够通信需要进行的所有修改。这个代码层就叫适配器。

1. 现实生活真的例子

如果你有一部智能手机或者一台平板电脑,在想把它(比如, iPhone手机的闪电接口)连接到你的电脑时,就需要使用一个USB适配器。

2. 软件的例子

Grok是一个Python框架,运行在Zope 3之上,专注于敏捷开发。 Grok框架使用适配器,让已有对象无需变更就能符合指定API的标准 。

Python第三方包Traits也使用了适配器模式,将没有实现某个指定接口(或一组接口)的对象转换成实现了接口的对象 。

3. 应用案例

在某个产品制造出来之后,需要应对新的需求之时,如果希望其仍然有效,则可以使用适配器模式。通常两个不兼容接口中的一个是他方的或者是老旧的。如果一个接口是他方的,就意味着我们无法访问其源代码。如果是老旧的,那么对其重构通常是不切实际的。更进一步,我们可以说修改一个老旧组件的实现以满足我们的需求,不仅是不切实际的,而且也违反了开放/封闭原则。 开放/封闭原则( open/closeprinciple)是面向对象设计的基本原则之一,声明一个软件实体应该对扩展是开放的,对修改则是封闭的。本质上这意味着我们应该无需修改一个软件实体的源代码就能扩展其行为。适配器模式遵从开放/封闭原则 。

因此,在某个产品制造出来之后,需要应对新的需求之时,如果希望其仍然有效,使用适配器是一种更好的方式,原因如下所示。

  • [ ] 不要求访问其他地方接口的代码
  • [ ] 不违反开放/关闭原则
4. 实现

我们的应用有一个Computer类,用来显示一台计算机的基本信息。这一例子中的所有类,包括Computer类,都非常简单,因为我们希望关注适配器模式,而不是如何尽可能完善一个类。

class Computer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} computer'.format(self.name)
def execute(self):
return 'executes a program'

在这里, execute方法是计算机可以执行的主要动作。这一方法由客户端代码调用 。

在Synthesizer类中,主要动作由play()方法执行。在Human类中,主要动作由speak()方法执行。为表明这两个类是外部的,将它们放在一个单独的模块中,如下所示。

class Synthesizer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} synthesizer'.format(self.name)
def play(self):
return 'is playing an electronic song'
class Human:
def __init__(self, name):
self.name = name
def __str__(self):
return '{} the human'.format(self.name)
def speak(self):
return 'says hello'

客户端仅知道如何调用execute()方法,并不知道play()和speak()。在不改变Synthesizer和Human类的前提下,我们该如何做才能让代码有效?适配器是救星!我们创建一个通用的Adapter类,将一些带不同接口的对象适配到一个统一接口中。init()方法的obj参数是我们想要适配的对象, adapted_methods是一个字典,键值对中的键是客户端要调用的方法,值是应该被调用的方法。

class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)

下面看看使用适配器模式的方法。列表objects容纳着所有对象。属于Computer类的可兼容对象不需要适配。可以直接将它们添加到列表中。不兼容的对象则不能直接添加。使用Adapter类来适配它们。结果是,对于所有对象,客户端代码都可以始终调用已知的execute()方法,而无需关心被使用的类之间的任何接口差别。

def main():
objects = [Computer('Asus')]
synth = Synthesizer('moog')
objects.append(Adapter(synth, dict(execute=synth.play)))
human = Human('Bob')
objects.append(Adapter(human, dict(execute=human.speak)))
for i in objects:
print('{} {}'.format(str(i), i.execute()))

现在来看看适配器模式例子的完整代码 。

class Synthesizer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} synthesizer'.format(self.name)
def play(self):
return 'is playing an electronic song'
class Human:
def __init__(self, name):
self.name = name
def __str__(self):
return '{} the human'.format(self.name)
def speak(self):
return 'says hello'
class Computer:
def __init__(self, name):
self.name = name
def __str__(self):
return 'the {} computer'.format(self.name)
def execute(self):
return 'executes a program'
class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)
def main():
objects = [Computer('Asus')]
synth = Synthesizer('moog')
objects.append(Adapter(synth, dict(execute=synth.play)))
human = Human('Bob')
objects.append(Adapter(human, dict(execute=human.speak)))
for i in objects:
print('{} {}'.format(str(i), i.execute()))
if __name__ == "__main__":
main()

输出如下:

the Asus computer executes a program
the moog synthesizer is playing an electronic song
Bob the human says hello

我们设法使得Human和Synthesizer类与客户端所期望的接口兼容,且无需改变它们的源代码。

5. 小结

我们使用适配器模式让两个(或多个)不兼容接口兼容 。无需修改不兼容模型的源代码就能获得接口的一致性。这是通过让一个通用的适配器类完成相关工作而实现的。

python设计模式之适配器模式的更多相关文章

  1. python 设计模式之适配器模式 Adapter Class/Object Pattern

    #写在前面 看完了<妙味>和<华医>,又情不自禁的找小说看,点开了推荐里面随机弹出的<暗恋.橘生淮南>,翻了下里面的评论,有个读者从里面摘了一段自己很喜欢的话出来, ...

  2. 浅谈Python设计模式 - 适配器模式

    声明:本系列文章主要参考<精通Python设计模式>一书,并且参考一些资料,结合自己的一些看法来总结而来. 从本篇便开始介绍结构型设计模式,而适配器设计模式便是该类设计模式的一种,那么什么 ...

  3. 最全36种python设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用.设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案.这些解决方案是众多软件开发人员经过 ...

  4. 每天一个设计模式-3 适配器模式(Adapteer)

    每天一个设计模式-3 适配器模式(Adapteer) 1.现实中的情况 旧式电脑的硬盘是串口的,直接与硬盘连接,新硬盘是并口的,显然新硬盘不能直接连在电脑上,于是就有了转接线.好了,今天的学习主题出来 ...

  5. Head First 设计模式之适配器模式与外观模式

    Head First设计模式之适配器模式与外观模式 前言: 之前讲过装饰者模式,将对象包装起来并赋予新的职责,这一章我们也会将对象进行包装,只不过是让它们看起来不像自己而像是别的东西.这样就可以在设计 ...

  6. C#设计模式(7)——适配器模式(Adapter Pattern)

    一.引言 在实际的开发过程中,由于应用环境的变化(例如使用语言的变化),我们需要的实现在新的环境中没有现存对象可以满足,但是其他环境却存在这样现存的对象.那么如果将“将现存的对象”在新的环境中进行调用 ...

  7. Java(Android)编程思想笔记02:组合与继承、final、策略设计模式与适配器模式、内部类、序列化控制(注意事项)

    1.组合和继承之间的选择 组合和继承都允许在新的类中放置子对象,组合是显式的这样做,而继承则是隐式的做. 组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形.即在新类中嵌入某个对象,让其实 ...

  8. 乐在其中设计模式(C#) - 适配器模式(Adapter Pattern)

    原文:乐在其中设计模式(C#) - 适配器模式(Adapter Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 适配器模式(Adapter Pattern) 作者:webabc ...

  9. C#设计模式之七适配器模式(Adapter)【结构型】

    一.引言   从今天开始我们开始讲[结构型]设计模式,[结构型]设计模式有如下几种:适配器模式.桥接模式.装饰模式.组合模式.外观模式.享元模式.代理模式.[创建型]的设计模式解决的是对象创建的问题, ...

随机推荐

  1. 手把手教你安装Office 2019 for Mac ,安装包和破解码都给你准备好了,还装不上的话,你找我!

    准备一个安装包,和一个破解工具 ​ 安装MicrosoftOffice16.23.19030902_Installer.pkg, 注意在断网情况下安装 同时不要自动更新 , 安装好之后不要打开文件!​ ...

  2. 02 安装net-tools工具

    01 登录虚拟机,没错,还是那个熟悉的黑窗口 02 输入用户名密码(我还是习惯使用root用户,因为,它可以为所欲为) 小知识:注意红色框内的符号: 一般用户为限制用户,符号为:$ 超级用户,为无限制 ...

  3. PyQt5模型视图委托

    Model-View-Delegate 模型视图委托(MVD)是PyQt中特有的设计模式,类似MVC设计模式,将MVC设计模式中的Controller当做MVD中的Delegate,两者的概念基本相同 ...

  4. vue组件库用markdown生成文档

    前言: 开发vue组件库需要提供组件的使用文档,最好是有渲染到浏览器的demo实例,既能操作又能查看源代码.markdown作为常用的文档编写载体,如果能在里面直接写vue组件,同时编写使用说明就再好 ...

  5. 完美解决pycharm 不显示代码提示问题

    pycharm 不显示代码提示 1.检查IDE省电模式是否关闭状态!!! file → power save mode 取消掉 2.检查代码提示是否成功开启. setting → Inspection ...

  6. .net core 使用 Serilog 作为日志提供者

    nuget引入 Serilog.AspNetCore Startup构造函数: public Startup(IConfiguration configuration) { Configuration ...

  7. 借鉴一个比较标准的后端RESTful API

    我们制定的 API 规范,使用了微服务架构所以做了一些改进,我们更偏向使用 http code 标识,不然需要自己处理成功或失败的逻辑,在 200 内再包一层显得啰嗦:并且微服务系列都不支持,Feig ...

  8. 构建一个基于事件分发驱动的EventLoop线程模型

    在之前的文章中我们详细介绍过Netty中的NioEventLoop,NioEventLoop从本质上讲是一个事件循环执行器,每个NioEventLoop都会绑定一个对应的线程通过一个for(;;)循环 ...

  9. Win10下ImageMagick及php-imageck扩展的安装

    安装ImageMagick https://imagemagick.org/script/download.php 选择符合自己电脑的版本进行安装即可.安装的时候注意勾选下面的选项自动加入环境变量,否 ...

  10. animation动画汇总(一阶段项目)

    animation 属性 动画属性: 1.animation-name:规定需要绑定到选择器的 keyframe 名称. 2.animation-duration:规定完成动画所花费的时间,以秒或毫秒 ...