大家好,我肥来了, 经历过一年的成长,我已经不是原来的我啦,以后会继续为大家带来更多的技术教程。
什么是适配器模式?
不用说都知道,适配器模式的核心并不在于器模式,而在于适配,为什么需要适配,哪些场景我们需要用到适配?
首先我们举个硬件上的例子,在typec出来之前,我们见到的绝大多数耳机的插头都是圆的,数码圈称之为极为先进的3.5毫米耳机孔,为什么一定要是圆的呢,插头做成方的不可以吗?答案是可以的,你当然可以做成方的,你把耳机孔做成六边形都没人管你,你说巧不巧,科学家刚好研制出了一款六边形插头的耳机,比传统的耳机音质好上一万倍,但是问题出现了,六边形的耳机插头对于市面上来说就是一种全新的方案,即使你这个技术多么的先进,但是一定会遇到没有手机能用的情况,为啥,因为市面上所有的手机的耳机孔都是圆的或者方的,根本没有六边形耳机孔的手机,但是我想用上全新的音质爆炸的六边形插头耳机怎么办?
注意,重点来了,搞一根3.5毫米接口转六边形接口的转接线去适配,注意这个适配,因为这根转接线的存在,对于手机而言,一切并没有什么不同,因为还是3.5毫米的插头接进来了,但是巧妙地是,通过适配器,也就是这跟转接线,我们让旧的系统无缝衔接上了新的技术。
所以,适配器的作用一定是提供了一种新旧系统之间的桥梁,从而让旧系统无需任何改变就可以扩展得到新的功能。在上面的例子中,旧系统指的是手机,我们无法改变,新技术指的是六边形的耳机插头,适配器就是3.5毫米转六边形的转接线。
代码实战
实战背景
让我们回到软件层面,假设某个公司有这样一套正在运营的图像处理系统,非常不巧的是,这套系统非常复杂,而且开发极其不规范,文档也没有,变量啥是啥也不知道,完全就是代码一时爽,重构火葬场,改完全无法下手,极端一点,如果我们改了原本的代码,系统分分钟冒出来一万个bug,原本这套系统只支持图像处理,现在我们要加上视频处理,以及音频处理,但是原来的代码我们又改不了,我又想新增这两个功能,怎么办呢?适配器模式就派上用场了。
我们可以在旧的api 和 新的系统之间加上一道桥梁,我们的应用依旧是调用同样的方法,一切都没有改变,但是内部的实现,我们已经悄悄的换上了新的实现,就类似于转接线的作用,我们接下来通过代码示例来更加深刻的去理解适配器模式。
首先我们先来看老系统关于图像处理的代码:
class Image:
def __init__(self, filename):
self.filename = filename
def execute(self):
print("开始处理图像")
复制代码
现在我们重新写两个类,代表新的视频处理功能以及音频处理功能:
class Video:
def __init__(self, filename):
self.filename = filename
def video_processing(self):
print("开始处理视频")
class Audio:
def __init__(self, filename):
self.filename = filename
def audio_processing(self):
print("开始处理音频")
复制代码
现在问题来了,老系统只会调用execute方法去执行任务,这点是写死了的,这个时候肯定有杠精,我把audio_processing这些方法改成execute不就好了吗,首先我们不说方法名不规范的问题,未来新增一个写日记的功能,你也要叫execute吗?也不是不可以,但是不建议这么做,而且这样的方法多了之后,系统会有非常多名字重复的方法,这样会让原本混乱的代码变得雪上加霜
方法名我也不能改,那我怎么去适配老系统的execute()的方法调用呢,接下来我们就要开始去写那根转接线,也就是我们的适配器了。
class Adapter:
def __init__(self,adapted_methods):
self.__dict__.update(adapted_methods)
复制代码
__dict__
魔法方法,将新组件对象的方法添加到适配器对象的属性字典中。这样就可以使用适配器对象即可调用新组件的方法。
没了?
…看来就是这么简单,当然,你也可以把对象传进去,去做一些额外的处理:
class Adapter:
def __init__(self,obj,adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
# 额外处理,返回对象信息
return str(self.obj)
复制代码
我们这里仍然去选择最简陋的那一版。
接下来就要开始我们的转接了。
audio = Audio('moog')
Adapter(dict(execute=audio.audio_processing)).execute()
复制代码
运行你会发现,控制台打印出了”**开始处理音频”**的信息,你看,我们明明调用的是execute()方法,但是真正执行的方法却是audio_processing方法,所以我们老系统只需要依然调用execute()就可以了,而内部的逻辑我们已经悄悄地换成了新的处理逻辑。
其实原理非常简单,主要是方法重命名,如果不是类的调用,只是简单的方法调用,你甚至可以这样也能起到相同的效果,不过非常不推荐。
synth = Audio('moog')
execute = synth.audio_processing
execute()
# 运行结果
# 开始处理音频
复制代码
没有啦,接下来我们进入总结:
总结
适配器好用吗,很好用,针对于哪些无法改动的项目,适配器模式的合理应用让旧的系统得到拓展变得简单,但是在上面的学习中,我们也逐渐发现适配器模式貌似也没有那么好,毫无疑问,它会让我们的项目逻辑变得混乱,我们调用的老的方法,内部却换成了新的实现,明明应该是audio_processing,对外却表现的是execute。
如果你是刚开始接触项目,你看到audio_processing大概率就会明白这个方法是处理音频相关的,而你看到execute却无法得到任何有价值的信息。
如果适配器在内部特别特别多的话,我们的代码逻辑将变得混乱不堪,所以能重构的情况下,去重构老项目去适配新的接口还是重构会更加好点,如果原来的代码没有办法去做改动,那么适配器可能是一个非常合适的选择。
因为直接把六边形插头的耳机插到支持六边形耳机孔的手机上,一定要比先把六边形耳机插到3.5毫米转六边形耳机转接线上,再把转接线的3.5毫米插到只支持3.5毫米耳机孔的手机上要简单的多,因为前一句我只打了27个字就说明白了,加了转接线我打了51个字。
最后非常感谢能读到这里的朋友,你们的支持和关注是我坚持高质量分享下去的动力。
韩数的所有笔记都已经上传至本人github。一定要点个star啊啊啊啊啊啊啊
万水千山总是情,给个star行不行
韩数的开发笔记
欢迎点赞,关注我,有你好果子吃(滑稽)