设计模式之--适配器模式

来源:互联网 发布:手机校色软件 编辑:程序博客网 时间:2024/04/30 02:40

设计模式--适配器模式


1、定义

适配器模式将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。

2、模式中涉及的角色

目标接口:与客户接触的接口,换句话说,客户只会使用这个接口

被适配接口:需要转换成客户所期望的那样的接口

适配器:通过包装一个需要适配的接口,把该接口转换成目标接口


3、适配器的类型

类适配器:继承被适配者和目标类(多继承)来实现

对象适配器:使用组合来适配被适配者来实现

由此可见,类适配器是通过多重继承来实现的,而在java中这是不可能的,所以,这里只谈对象适配器


4、适配器模式(对象)类图

5、现实应用与举例

    有这么一个情景:一个客户拥有三台设备,分别是小米5、苹果手机、MP3,当他想为这三个设备充电时就得拥有三条对应的充电线,即各自的线的接口只适合自己用,不适合其他设备用。代码如下:

/** * 不用适配器的情况 * @Author 先 * @ClassName Client_NotAdapter.java * @Time 2017年3月11日 下午9:33:58 */public class Client_NotAdapter { public static void main(String[] args) {//给小米5充电,生成小米5专用充电线ElectryLine mi_line = new Xiaomi5();mi_line.electricize();//给苹果手机充电,生成苹果手机专用充电线ElectryLine apple_line = new Apple();apple_line.electricize();//给MP3充电,生成MP3专用充电线ElectryLine mp3_line = new MP3();mp3_line.electricize();}}/** * 充电线 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:33:06 */interface ElectryLine{//充电方法void electricize();}/** * 小米5手机 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:36:14 */class Xiaomi5 implements ElectryLine{@Overridepublic void electricize() {System.out.println("小米5正在充电……");}}/** * 苹果手机 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:37:15 */class Apple implements ElectryLine{@Overridepublic void electricize() {System.out.println("苹果手机正在充电……");}}/** * MP3 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:38:05 */class MP3 implements ElectryLine{@Overridepublic void electricize() {System.out.println("MP3正在充电……");}}

上述做法的缺点:

1、客户得创建三个对象(充电线),耗费内存空间多

2、没有将目标类与适配者类解耦

而使用适配器模式,就可以只用一条充电线,给三台不同接口的设备充电,就像这张图(网上随便找的一张)

这张图很好的解释了适配器的定义与应用,代码如下:

/** * 适配器模式 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:26:52 */public class Client_Adapter {//客户public static void main(String[] args) {Xiaomi5 xiaomi = new Xiaomi5();Apple apple = new Apple();MP3 mp3 = new MP3();//这句话可以这么理解:充电线的引用指向了适配器,相当于将充电线插到转接头上ElectryLine line = new Adapter(xiaomi,apple,mp3);//如果客户想给小米5充电line.electricize("xiaomi5");//如果客户想给苹果手机充电line.electricize("apple");//如果客户想给MP3充电line.electricize("mp3");//总结分析:整个过程,客户只创建了一个充电线的对象,却通过了适配器(即转接头)给三台不同的设备充电了//实际上,这个适配器只是把一个接口转换成另一个接口了(并没有同时转换成三个接口),//只不过是通过传参的形式来判断到底要转换成哪个接口,转换的只是小米5或者苹果或者MP3//本里这样子写只是为了让读者更好地理解适配器}}/** * 充电线 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:33:06 */interface ElectryLine{//充电方法void electricize(String type);}/** * 小米5手机 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:36:14 */class Xiaomi5{public void mi_electry(){System.out.println("小米5正在充电……");}}/** * 苹果手机 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:37:15 */class Apple{public void apple_electry(){System.out.println("苹果手机正在充电……");}}/** * MP3 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:38:05 */class MP3{public void mp3_electry(){System.out.println("MP3正在充电……");}}/** * 这里假设这个适配器就只是转接小米5、苹果和MP3的 * @Author 先 * @ClassName Client_Adapter.java * @Time 2017年3月11日 下午8:40:15 */class Adapter implements ElectryLine{//对象适配器使用的是组合语法Xiaomi5 xiaomi;Apple apple;MP3 mp3;//用构造器取得被适配对象的引用public Adapter(Xiaomi5 xiaomi,Apple apple,MP3 mp3){this.xiaomi = xiaomi;this.apple = apple;this.mp3 = mp3;}@Overridepublic void electricize(String type) {switch(type){case "xiaomi5" : xiaomi.mi_electry();break;case "apple" : apple.apple_electry();break;case "mp3" : mp3.mp3_electry();break;}}}
分析:

看起来使用了适配器模式后代码变多了,但是将目标类与适配者类分开了,即将充电线与手机分开,客户指操作一条线,中间通过转接口(适配器)来给不同的设备充电,对于客户来说,体验好了,从内存的角度来说,也节省了空间。正如《Thinking in java》中所说--“适配器接受你任意的接口类型,并且产生你需要的接口类型”,这里就验证的这句话,转接头(适配器)接受不同的设备接口,生成客户想要的那个接口(目标接口ElectryLine)。

再用代码来解释本文上述的模型图:

/** * 目标接口 * @Author 先 * @ClassName Client_NotAdapter.java * @Time 2017年3月11日 下午11:18:35 */interface Target{void request();}/** * 被适配类 * @Author 先 * @ClassName Client_NotAdapter.java * @Time 2017年3月11日 下午11:19:53 */class Adaptee{public void specificRequest(){}}/** * 适配器 * @Author 先 * @ClassName Client_NotAdapter.java * @Time 2017年3月11日 下午11:21:19 */class Adapter implements Target{Adaptee adaptee;public Adapter(Adaptee adaptee){this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}}

6、总结:

优点:

1、通过适配器,客户可以调用同一个接口,来操作本来接口不匹配的对象,而这些实现对客户是透明的。

2、复用了现存的类,解决了现存类和复用环境要求不一致的问题。

3、将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码。
4、一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。


总而言之,就是

更好的复用性

系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

更好的扩展性

在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。


缺点:

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

0 0
原创粉丝点击