ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

设计模式之【适配器模式】

2022-03-19 12:02:45  阅读:179  来源: 互联网

标签:角色 适配 适配器 接口 public 模式 设计模式 Adaptee


我们生活中很熟悉的转换插头,因为全球有几种不同的插座接口标准,国内使用的插头不一定适用于国外一些国家的插座,这时候,就需要用到转换插头了。我手中的这个就是德标转国标的。

 

你看,这不就类似我们设计模式中的适配器模式嘛?


将一个类的接口转换成客户希望的另外一个接口,使得原来由于接口不兼容而不能一起工作的那些类可以一起工作。

文字描述不容易理解没关系,通过下图,应该会有一个更直观的认识。

 

需求总是会不断地变化,现在我们需要从旧厂商类切换到新厂商类,由于现有的系统与新厂商类彼此不兼容,修改某一个类以兼容另外一个类,都不是件轻松的事。这时候,就需要适配器模式来“救场”了。你看,只需要增加一个适配器的代码,就可以了,很轻松。

有了直观的认识之后,我们就来好好地认识一下适配器模式。

 

  • Target目标角色:该角色定义把其他类转换为何种接口,也就是我们的期望接口。

  • Adaptee源角色:你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的,运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新的角色。

  • Adapter适配器角色:适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:就是把源角色转换为目标角色。

适配器模式有两种实现方式:类适配器对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现。

类适配器

类适配器的重点在于类,是通过构造一个继承Adaptee类来实现适配器的功能。

Adaptee:小明进入NBA了,但是他只会用中文沟通,

 1 // 已存在的,但是不符合我们既有的标准接口的类
 2 public class XiaoMing {
 3     private int Num;
 4     private String name;
 5     
 6     // 只会用中文沟通
 7     public void Chinese() {
 8         System.out.println("用中文沟通");
 9     }
10 }

Target:但是球队教练是法国人,只会法语和英语,不用用中文。

1 // 目标接口,或称为标准接口
2 public interface Coach {
3     void English();
4     void French();
5 }

Adapter:球队给小明安排了一位翻译。

 1 // 适配器类:继承了被适配类,同时实现了标准接口
 2 public class Interpreter extends XiaoMing implements Coach {
 3     
 4     @Override 
 5     public void English() {
 6         // 和球员小明用中文沟通
 7         super.Chinese();
 8         System.out.println("和教练用英语沟通");
 9     }
10     
11     @Override 
12     public void French() {
13         // 和球员小明用中文沟通
14         super.Chinese();
15         System.out.println("和教练用法语沟通");
16     }
17 }

客户端

 1 public class Demo {
 2     public static void main(String[] args) {
 3         Coach interpreter = new Interpreter();
 4         
 5         // 翻译是双方沟通的桥梁
 6         interpreter.Chinese();
 7         interpreter.English();
 8         interpreter.French();
 9     }
10 }

但是,由于Java中不支持多继承,所以这个适配器(翻译)只能服务于所继承的被适配者(小明)。如果球队里有很多不会讲英语和法语的球员,每个球员都安排一位翻译的话,那成本就会很大,那有什么办法解决呢?

这个对象适配器就派上用场了。

对象适配

对象适配器的重点在于对象,是通过在直接组合Adaptee类来实现的,而不需要继承被适配的类。而是通过在适配器的构造函数中将需要被适配的类传递进来,从而进行适配。

Adapter代码如下,Target和Adaptee的代码同上。

 1 public class Interpreter implements Coach {
 2     // 与类适配器不同的是,对象适配器可以适配多个源到目标
 3     private Xiaoming xiaoming;
 4     // 比如,还可以组合其他Adaptee
 5     // private Tony tony;   // 讲俄语
 6     
 7     // 在构造函数中将Adaptee类Xiaoming传递进来
 8     public Interpreter(Xiaoming xiaoming) {
 9         this.xiaoming = xiaoming;
10     }
11     
12     // 实现接口中的方法
13     @Override
14     public void English() {
15         // 和球员小明用中文沟通
16         this.xiaoming.Chinese();
17         // 和教练用英语沟通
18         System.out.println("用英语沟通");
19     }
20     
21     @Override 
22     public void French() {
23         // 和球员小明用中文沟通
24         this.xiaoming.Chinese();
25         // 和教练用法语沟通
26         System.out.println("用法语沟通");
27     }
28 }

客户端

1 public class Demo {
2     public static void main(String[] args) {
3         Coach interpreter = new Interpreter(new XiaoMing());
4         
5         interpreter.English();
6         interpreter.French();
7     } 
8 }

两种方式应该如何选择?

判断的标准主要有两个,一个是Adaptee接口的个数,另一个是Adaptee和Target的契合程度。

  • 如果Adaptee接口并不多,那两种实现方式是都可以;

  • 如果Adaptee接口很多,而且Adaptee和Target接口定义大部分都相同,那我们推荐使用类适配器,因为Adapter复用父类Adaptee的接口,比起对象适配器的实现方式,Adapter的代码量要少一些。

  • 如果Adaptee接口很多,而且Adaptee和Target接口定义大部分都不相同,那我们推荐使用对象适配器,因为组合结构相对于继承更加灵活。

适配器模式的优点

  • 适配器模式可以让两个没有关系的类在一起运行,只要适配器这个角色能够搞定他们就可以。

  • 增加了类的透明性

    想想看,我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。

  • 提高了类的复用度

    源角色在原有的系统中还是可以正常使用的,而在目标角色中也可以充当新的演员。

  • 灵活性非常好

    如果突然不想要适配器,没有问题,删除掉这个适配器就可以了,其他的代码都不用修改,基本上就类似一个灵活的构件,想用就用,不用就卸载。

适配器模式的缺点

  • 对于类适配器来说,由于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为接口,不能为类,其使用有一定的局限性,不能将一个适配者类和它的子类同时适配到目标接口。

  • 对于对象适配器来说,与类适配器模式相比,要想置换适配者类的方法就不容易。

  • 过多地使用适配器,会让系统非常凌乱,不容易整体进行把握。

    比如,明明看到调用的是A接口,其实内部被适配成了B接口来实现,一个系统如果出现太多这种情况,无异于一场灾难。

适配器模式的应用场景

一般来说,适配器模式可以看作是一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。如果在设计初期,就能协调规避接口不兼容的问题,那么,适配器模式就没有应用的机会了。

  1. 封装有缺陷的接口设计

    如果我们依赖的外部系统在接口设计方面有缺陷(比如包含大量静态方法),引入之后会影响到我们自身代码的可测试性。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。

  2. 统一多个类的接口设计

    某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。

  3. 替换依赖的外部系统

    当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。

  4. 兼容老版本接口

    在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的项目有个过渡期,而不是强制进行代码修改。这也可以粗略地看做适配器模式的一个应用场景。

  5. 适配不同格式的数据

    适配器模式主要用于接口的适配,实际上,它还可以用在不同格式的数据之间的适配。比如,把从不同征信系统拉取的不同格式的征信数据,统一为相同的格式,以方便存储和使用。再比如,Java中的Arrays.asList()也可以看作一种数据适配器,将数据类型的数据转化为集合容器类型。

总结

适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。

参考

《设计模式之禅》

极客时间专栏《设计模式之美》

《Head First 设计模式》

https://www.jb51.net/article/100146.htm

 

标签:角色,适配,适配器,接口,public,模式,设计模式,Adaptee
来源: https://www.cnblogs.com/Gopher-Wei/p/16025896.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有