设计模式-适配器模式

设计模式-适配器模式

定义

适配器模式(Adapter Pattern)又叫做变压器模式,它的功能是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而导致无法在一起工作的两个类能够一起工作.

属于结构型设计模式.

也就是说,当前系统存在两种接口A和B,客户只支持访问A接口,但是当前系统没有A接口对象,但是有B接口对象,但客户无法识别B接口,因此需要通过一个适配器C,将B接口内容转换成A接口,从而使得客户能够从A接口获取得到B接口内容.

在软件开发中,基本上 任何问题都可以通过增加一个中间层进行解决.适配器模式就是一个中间层.综上,适配器模式其实起着转化/委托的作用,将一种接口转化为另一种符合需求的接口.

适配器模式一般包含3种角色:目标角色、源角色、适配器.

1、目标角色(Target):也就是我们期望的接口;

2、源角色(Adaptee):存在于系统中,内容满足客户需求(需转换),但借口不匹配的接口实例;

3、适配器(Adapter):将源角色(Adaptee)转化为目标角色(Target)的类实例.

适配器模式各角色之间的关系如下:

假设当前系统中,客户端需要访问的是target接口,但target接口没有一个实例符合需求,而Adaptee实例符合需求;但是客户端无法直接使用Adaptee(接口不兼容);因此,我们需要一个适配器(Adapter)来进行中转,让Adaptee能转化为Target接口形式.

适配器模式有3种形式:类适配器对象适配器接口适配器.

类适配器

类适配器的原理就是通过继承来实现适配器的功能.具体做法:让Adapter实现Target接口,并且继承Adaptee,这样Adapter就具备Target和Adaptee等特性,就可以将两者进行转化.

类适配器UML图

下面我们以一个实例进行讲解,来看下该实例分别用类适配器、对象适配器和接口适配器是怎样进行代码实现.在中国民用电都是220V交流电,但我们手机使用的锂电池使用的5V直流电.因此我们给手机充电时就需要使用电源适配器来进行转换.下面我们有代码来还原这个生活场景,创建Adaptee角色,需要被转换的对象AC220类,表示220V交流电:

Adaptee角色:需要被转换的对象AC220类
Target角色:DC5接口,表示5V直流电的标准
Adapter角色:电源适配器PowerAdapter类
测试
运行结果

上面的案例中,通过增加PowerAdapter电源适配器,实现了两者的兼容.

对象适配器

对象适配器的原理就是通过组合来实现适配器的功能.具体做法:让Adapter实现Target接口,然后内部持有Adaptee实例,然后在Target接口规定的方法那转换Adaptee.

接口适配器:只实现target接口,并持有Adaptee对象.
测试方法

接口适配器

接口适配器的关注点与类适配器和对象适配器不太一样,类适配器和对象适配器着重于系统存在一个角色(Adaptee)转化成目标接口(Target)所需内容,而接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,类显得很臃肿.此时,使用接口适配器就能让我们只实现我们需要的接口方法,目标更清晰.

接口适配器的主要原理就是利用抽象类实现接口,并且空实现接口众多方法.下面我们来接口适配器的源码实现,首先创建Target角色DC5类:

Target角色:DC5类(具有多个接口)
Adaptee角色:需要被转换的对象AC220类
Adapter角色:PowerAdapter类.
测试代码,运行结果同上.

重构第三方登录自由适配的业务场景

下面我们来一个实际的业务场景,利用适配器模式来解决实际问题.年纪稍微大一点的小伙伴一定经历过这样一个过程,我们很早以前开发的老系统应该都有登录接口,但是随着业务的发展和社会的进步,单纯的依赖用户名密码登录显然不能满足用户需求了.现在,我们大部分系统都支持多种登录方式,如QQ登录、微信登录、手机登录、微博登录等等,同时保留用户名密码登录方式.虽然登录方式丰富了,但是登录后的处理逻辑可以不必改,同样是将登陆状态保存到session,遵循开闭原则,我们开始创建统一的返回结果ResultMsg类:

创建统一的返回结果ResultMsg类
老系统的登录逻辑PassportService

为了遵循开闭原则,老系统的代码我们不会去修改.那么下面开启代码重构之路,先创建Member类:

用户信息类

运行非常稳定的代码我们不去改动,首先创建Target角色IPassportForThird接口:

Target角色IPassportForThird接口:支持了多种登录方式

然后创建适配器Adapter角色实现兼容,创建一个新的类PassportForThirdAdapter继承原来的逻辑:

Adapter角色:类PassportForThirdAdapter继承原来的逻辑和新登录方式
测试代码

通过这么一个简单的适配,完成了代码兼容.当然,我们代码还可以更加优雅,根据不同的登录方式,创建不同的Adapter.首先,创建ILoginAdapter接口:

LoginAdapter接口

然后,创建一个抽象类AbstractAdapter继承PassportService原有的功能,同时实现ILoginAdapter接口,然后分别实现不同的登录适配.

抽象类
QQ登录LoginForQQAdapter
手机登录:LoginForTelAdapter
Token自动登录:LoginForTokenAdapter
微信登录:LoginForWechatAdapter

然后,创建适配器PassportForThirdAdapter类,实现目标接口IPassportForThird完成兼容,

适配器PassportForThirdAdapter类
测试代码

最后我们看一下类图:


UML图(PS:mac本对uml工具支持真的有限啊,哪个大佬给推荐个好用的建模工具,谢过啦~)

至此,我们在遵循开闭原则的前提下,完整地实现了一个兼容多平台登录的业务场景.当然,我们目前的这个设计也并不完美,仅供参考,感兴趣的小伙伴们可以继续完善这段代码.例如适配器中的参数目前是写死成String的,改为Object[]会更合理.

学习到这里,相信小伙伴们会有一个疑问:适配器模式跟策略模式好像区别不大?在这里我要强调一下,适配器模式主要解决的是功能兼容问题,单场景适配大家肯定不会和策略模式有对比.但多场景适配大家容易产生联想和混淆.其实,大家有没有发现一个细节,我给每个适配器都加上了一个support()方法用来判断是否兼容,support()方法的参数也是Object的,而support来自于接口.适配器内的逻辑并不依赖于接口,我们完全可以讲ILoginAdapter接口去掉.而加上接口,只是为了代码规范.上面的代码可以说是策略模式、简单工厂和适配器模式的综合运用.

适配器模式在源码中的体现

Spring中适配器模式也应用的非常广泛,例如:SpringAOP中的AdvisorAdapter类,它有三个实现类MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter和ThrowsAdviceAdapter,先来看顶层接口AdvisorAdapter的源代码:

AdvisorAdapter接口

再看MethodBeforeAdviceAdapter类:

MethodBeforeAdviceAdapter类
AfterReturningAdviceAdapter类

剩余的那个类就不贴了,Spring会根据不同的AOP配置来确定使用对应的Advice,跟策略模式不同的是:策略模式一个方法可以同时拥有多个Advice.

扩展:SpringMVC中的HandlerAdapter类也是一个适配器模式的应用实例.

适用场景

1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况.

2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案.

生活中也存在非常多的应用场景,例如电源转换插头、手机充电转换头,显示器转接头等.

优点

1、能提高类的透明性和复用性,现有的类复用但不需要改变;

2、目标类和适配器类解耦,提高程序的扩展性;

3、在很多业务场景中符合开闭原则.

缺点

1、适配器编写过程需要全面考虑,可能会增加系统的复杂度;

2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱.

适配器模式和装饰器模式对比

装饰器是一种非常特别的适配器
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容