设计模式之适配器模式(Adapter Pattern)

在正式开始之前,让我们先思考几个问题:

  • 如果现有的新项目可以利用旧项目里大量的遗留代码,你打算从头开始完成新项目还是去了解旧项目的模块功能以及接口?
  • 如果你了解过遗留代码之后,发现有几个重要的功能模块接口不同(因为它们可能来自多个旧项目),无法直接复用,你打算放弃使用遗留代码吗?
  • 如果你不打算放弃(这样做应该是对的,毕竟遗留代码的正确性是经过实践检验的),那么是不是只能去改写剩余的n - 1个接口,甚至改写所有的n个接口?
  • 如果不这样做,还有什么简单的方法吗?

一.什么是适配器模式?

首先,我们需要知道适配器是什么东西,嗯,笔记本电脑的电源适配器听说过吧?

它能够把220V的交流电转换为笔记本需要的15V直流电

太神奇了,一个小小的电源适配器解决了家庭用电与笔记本需要的电类型不匹配的问题

发现什么了么?

没错,我们既没有改变家庭用电(把它变成15V直流电),也没有改变笔记本(把它变成220V交流电),但我们确实解决了这个问题

-------

适配器模式——用来实现不同接口转换的设计模式

二.举个例子

假设我们有两个封装好的功能模块,但它们需要的参数不同(虽然参数的实质是同一种对象)

比如,我们的A模块(文本检查模块)是这样的:

package AdapterPattern;/*** @author ayqy* 文本检查模块(类似与MSOffice Word中的“拼写和语法检查”)*/
public class TextCheckModule {FormatText text;public TextCheckModule(FormatText text){this.text = text;}/** 省略很多具体Check操作。。*/
}

A模块入口需要一个FormatText类型的参数,它的定义如下:

package AdapterPattern;/*** @author ayqy* 定义格式化文本*/
public interface FormatText {String text = null;/*** @return 文本逻辑行数*/public abstract int getLineNumber();/*** @param index 行号* @return 第index行的内容*/public abstract String getLine(int index);/** 省略其它有用的方法*/
}

还有B模块(文本显示模块),它是这样的:

package AdapterPattern;/*** @author ayqy* 文本显示模块*/
public class TextPrintModule {DefaultText text;public TextPrintModule(DefaultText text){this.text = text;}/** 省略很多显示相关操作* */
}

B模块入口所需的DefaultText:

package AdapterPattern;/*** @author ayqy* 定义默认文本*/
public interface DefaultText {
String text = null;/*** @return 文本逻辑行数*/public abstract int getLineCount();/*** @param index 行号* @return 第index行的内容*/public abstract String getLineContent(int index);/** 省略其它有用的方法*/
}

我们的新项目要求实现一个文字处理程序(像MSOffice Word那样的),我们需要调用A模块来实现文本检查功能,还需要调用B模块来实现文本显示功能

但问题是,两个模块的接口不匹配,导致我们无法直接复用现成的A和B。。

这时我们似乎只有有两个选择:

  1. 修改FormatText(或者DefaultText),以满足DefaultText(或者FormatText),还需要修改A(或者B)的内部实现
  2. 定义第三种接口MyText,修改A和B,让它们把MyText作为参数,以求接口的统一

当然,我们可能更倾向与第一种,毕竟所需的修改相对较少,不过即使这样,工作量仍然很大,我们需要打开A的封装,理解其内部实现,并修改方法调用细节

-------

其实我们还有更好的选择——定义一个Adapter,负责FormatText到DefaultText的转换(或者与此相反):

package AdapterPattern;/*** @author ayqy* 定义默认文本适配器*/
public class DefaultTextAdapter implements DefaultText{FormatText formatText = null;//源对象/*** @param text 需要转换的源文本对象*/public DefaultTextAdapter(FormatText formatText){this.formatText = formatText;}@Overridepublic int getLineCount() {int lineNumber;lineNumber = formatText.getLineNumber();/** 在此添加额外的转换处理* */return lineNumber;}@Overridepublic String getLineContent(int index) {String line;line = formatText.getLine(index);/** 在此添加额外的转换处理* */return line;}
}

我们的做法其实相当简单:

  1. 定义Adapter实现目标接口
  2. 获取并保留源接口对象
  3. 实现目标接口中的各个方法(在方法体中调用源接口对象的方法并添加额外的处理以实现转换)

适配器做好了,要怎么用呢?不妨实现一个Test类来测试一下:

package AdapterPattern;/*** @author ayqy* 测试接口适配器*/
public class Test implements FormatText{public static void main(String[] args) {//创建源接口对象FormatText text = new Test();//创建文本检查模块对象TextCheckModule tcm = new TextCheckModule(text);/*调用tcm实现文本检查*///创建适配器对象,进行源接口对象到目标接口对象的转换DefaultTextAdapter textAdapter = new DefaultTextAdapter(text);//用Adapter创建文本显示模块对象TextPrintModule tpm = new TextPrintModule(textAdapter);/*调用tcm实现文本显示*/}/*请忽略下面偷懒的部分。。*/@Overridepublic int getLineNumber() {// TODO Auto-generated method stubreturn 0;}@Overridepublic String getLine(int index) {// TODO Auto-generated method stubreturn null;}}

(P.S.原谅我的偷懒行为,谁让FormatText偏偏是个接口呢。。)

当然,Test是不会有运行结果的,但能通过编译就足够说明我们的转换没有问题。。

-------

其实我们忽略了一个很重要的问题,例子中源接口与目标接口的方法都是对应的,换句话说就是:源接口中定义的方法在目标接口中都有类似的方法与之对应

当然,这样的情况是极少的,通常都存在方法不对应的问题(源接口中存在目标接口未定义的方法,或者相反的情况)

这时我们有2个选择:

  • 抛出异常,但应该在注释或者文档作出详细说明,就像这样:
throw new UnsupportedOperationException();//源接口不支持此操作
  • 完成一个空的实现,比如,return false,0,null等等

具体选择哪一种,取决于具体情景,各有各的好处,不能一概而论

三.另一种适配器实现方式

例子中我们采用了“持有源接口对象,实现目标接口”的方式来实现适配器,其实还存在另一种方式——多继承(或者实现多个接口)

如果一个Adapter类既实现了A接口又实现了B接口,那么,毫无疑问,Adapter对象既属于A类型又属于B类型(多继承的原理类似。。)

虽然Java不支持多继承,但在支持多继承的语言环境下我们应当想到这样的实现方式,再视具体情况决定是否采用多继承来实现Adapter

四.总结

当我们手里同时握着一个两孔插头和一个三孔插口时,总是习惯把插头芯拧成八字形的。为什么不去买一个适配器呢?

  • 既不需要破坏插头,也不需要破坏插口(有时代码修改确实是破坏性的,我们避免了修改也就避免了破坏)
  • 更关键的是:我们可以把买来的适配器借给朋友用(可复用)

转载于:https://www.cnblogs.com/ayqy/p/3971725.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/492910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

错位排列-信封问题

五个编号为1~5的小球放进5个编号为1~5的小盒里面,全错位排列(即1不放1,2不放2,依次类推)一共有多少种放法 这是著名的信封问题,很多著名的数学家都研究过瑞士数学家欧拉按一般情况给出了一个递推公式: 用A…

存在描述所有生命的方程吗?

○ 生物体既复杂又有序,就像这片亚麻茎的横截面。研究人员希望最终发展出描述所有生命的基本方程。来源:原理关于生命,没有什么是简单的。每一秒钟,都有数百万个缜密有序的化学反应发生在一个细胞内;数十亿个单细胞生物…

asp.net(C#)写SQL语句技巧

/*添加SQL*/string fields "";string values "";fields "xm"; values "" Request.Form["xm"].ToString() "";fields ",xb"; values "," Request.Form["xb"].ToString() …

Python函数参数值传递

Python的函数参数是通过值传递的,但是如果变量是可变对象,返回到调用程序后,该对象会呈现被修改后的状态 测试程序如下: # 值传递不改变变量 def addInterest(balance, rate):newBalance balance * (1rate)return newBalance de…

CSS3滤镜

今天在办公室亲眼目睹了同事使用CSS3滤镜为一张漂亮的照片轮廓加上了阴影,瞬间亮瞎了我的的双眼,见笑了。 所以也迅速尝试使用CSS3滤镜让最新出炉的MUI LOGO也性感一把,试图来愉悦一下大家的双眼。已经等不及了,赶紧奉上今天的测试…

Python中递归字符串反转

我们可以用Python轻松的反转字符串,代码如下: def reverse(s):if s :return selse:return reverse(s[1:]) s[0]print(reverse(hello))运行结果如下:Connected to pydev debugger (build 143.1559) ollehProcess finished with exit code 0

全面认识“边缘云”,中国首份边缘云白皮书发布

来源:中国电子技术标准化研究院、阿里云摘要:《边缘云计算技术与标准化白皮书》近期发布,定义了边缘云计算的概念和标准等。中心云和边缘云相互配合,实现中心-边缘协同、全网算力调度、全网统一管控等能力,真正实现“无…

Python中异常处理

Python使用try...except...来处理异常,当Python遇到一个try语句,他会尝试执行try语句体内的语句,如果执行这些语句没有错误,控制转移到try...except...后面的语句,如果语句体内发生错误,python会寻找一个符…

【Tech】Mac上安装MAMP打开本地网页

不知道为什么实验室老是用些奇葩的东西,这次是madserve,主要是用来统计移动端广告点击率的,基于PHP/MYSQL实现。 昨天很快在Windows上搭好一个xampp,并用它建立了一个virtual host把madserve跑起来了。但是在mac上xampp建立virtua…

中国10大最震撼的无人工厂,你吃的用的都是这么来的!

来源:工业机器人摘要:你吃的用的那么多东西,早已不是“人造”的!1.上海通用金桥工厂:386台机器人图中展示的是上海通用金桥工厂。这里号称中国最先进的制造业工厂、中国智造的典范。即使从全球来看,这个水平…

图像拼接2 特征匹配

特征匹配旨在从两幅图像所提取的特征集中寻找对应关系,匹配过程是从前面的图像特征描述符的相似性来进行判断。 判断两个图像特征集中的特征点的相似程度存在两种基本方式: 1:给定阈值,即从特征集中找出所有与待匹配点距离小于阈…

状态压缩 之 UVA 10944 - Nuts for nuts..

// [9/19/2014 Sjm] /* dis[j][k] : 从 j 点到 k 点的最少步数,由于They can travel in all 8 adjacent direction in one step.故而 dis[j][k] max( abs(Xj - Xk), abs(Yj - Yk) )f[j][i] : 在 i 状态下,最后收集坚果 j 的最少步数n 代表坚果的数目。…

对人工神经网络“开刀”,利用神经科学消融法检测人工神经网络

来源:DeepTech深科技摘要:当谈及人工神经网络,黑箱问题总会引起热议,人们对黑箱问题的评价褒贬不一。有人认为黑盒是神经网络的优势,这代表神经网络的自主学习性,代表其自动学习以及自动完善的特性。但大部…

wp8数据存储--独立存储文件 【转】

出自 : http://www.cnblogs.com/MyBeN/p/3339019.html 文章篇幅有点大,建议去源网看看 1.调用手机的独立存储 例如:IsolatedStorageFile storage IsolatedStorageFile.GetUserStoreForApplication() 2.创建独立存储文件流 例如:I…

从车联网到工业智联网

来源:智车科技摘要:本文从工业互联网的典型应用—车联网谈起,从工业网联技术发展过程的视角分析了工业智联网的构架、关键技术和前沿趋势,对智联网视域下的未来智联交通作出了展望。随着智能技术的发展,从工业互联网发…

图像拼接1 特征提取

图像特征提取在图像拼接中占据至关重要的地位,旨在从图像中提取显著特征并用数学语言加以描述。 图像特征包括:特征点、边缘、轮廓、闭合区域以及统计特征等。 一般来说,特征点是指图像中具有某些特性的结构特征,特征点既可以是…

清华发布《AI芯片技术白皮书》:新计算范式,挑战冯诺依曼、CMOS瓶颈

来源:机器人 悦智网摘要:在由北京未来芯片技术高精尖创新中心和清华大学微电子学研究所联合主办的第三届未来芯片论坛上,清华大学正式发布了《人工智能芯片技术白皮书(2018)》。《白皮书》首次整合了国际化的学术和产业资源,紧扣学…

hdu 3944 DP? (Lucas 定理)

仔细观察杨辉三角后可以发现从最高点到第n行第k个数的最短路为c(n1,k); 根据Lucas定理可以求出,一般来说要求答案模去一个质数p且p的范围不大于10^5则可用Lucas. Lucas(n,m,p)cm(n%p,m%p)* Lucas(n/p,m/p,p)Lucas(x,0,p)1;另外注意当k>n/2时,必须令kn…

常用的编码

ASCII码是标准化字符集,使用7个二进制位编码,可以表示128个字符,通常ASCII表示的是英文字母的二进制编码, 例如大写字母A的二进制编码为65,python代码如下: >>> ord(A) 65 >>> chr(65) …

这是一份 AI 界最强年终总结

来源:AI 科技评论圣诞节元旦假期过后,谷歌资深 Fellow、谷歌 AI 负责人 Jeff Dean 代表所有谷歌的研究部门发出了他们的 2018 年度科研研究年终总结。这一年,谷歌的科研人员们在人工智能、量子计算、计算图形学、算法理论、软件系统、TPU、开…