• [设计模式]2、设计模式之适配器设计模式


    适配器模式应用场景举例 

            MM的表格炒股发的一塌糊涂,答应MM送她一款笔记本。这下可把MM乐坏了^_^不过因为表哥和MM上学的学校不在同一个城市,所以笔记本需要邮寄过来。几天后,MM终于收到了笔记本,快要开心死了!但当MM正要查看电脑的实际运行效果的时候,却发现插头不对,无法插进去!哈哈,MM欲哭无泪,立即打电话给GG。GG在得知了事情的详细经过后,笑着说道:“傻瓜,别急,我去给你买一个插头转换器不久OK了吗”,说完,GG就跑到科技市场,寻找适合MM电脑的插头转换器,也不知道MM的表哥在哪里买的这个电脑,问了好几个店铺都没找到合适的,GG心想,如果买不到就无法回去向MM交代啊。最终在GG千辛万苦的努力下还是买到了。当MM终于能够使用电脑的时候,给GG发了一条短信“亲爱的,爱死你了,下辈子还爱你!”。

    适配器模式解释: 

           适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

           适配器分为对象适配器和类适配器。

           对象适配器使用组合方式,不仅可以适配某个类,也可以适配该类的任何子类,如果子类中加入新的行为,只需要让组合对象是子类就可以解决灵活性的问题;而类适配器因为采用的是继承的方式,所以只能够采用某个特定的被适配类,这就有一个很大的优点,那就是不需要实现整个被适配类,而且在必要的时候还可以覆盖被适配者的行为。

    英文定义为:Convert the interface of a class into another interface clients expect.

    适配器模式的UML图: 

           类适配的UML图如下 所示:

     

           类适配器所涉及的角色如下:

           目标(Target)角色:是客户端期待得到的接口。因为这里讨论的是类适配器,又由于Java语言单继承的属性,因此,目标角色不可以是类。

           源(Adaptee)角色:需要适配的接口,就是被适配的对象。

           适配器(Adapter)角色:这是类适配器模式的核心。适配器把源接口转换成目标接口,很显然,适配器角色不可以是接口,而必须是具体的类。

    对象适配器的UML图如下所示:

    对象适配器所涉及的角色如下:

           目标(Target)角色:是客户端期待得到的接口。目标角色可以是具体类也可以是抽象类。

           源(Adaptee)角色:需要适配的接口,就是被适配的对象。

           适配器(Adapter)角色:这是对象适配器模式的核心。适配器把源接口转换成目标接口,很显然,适配器角色不可以是接口,而必须是具体的类。

    适配器模式深入分析

           我们经常碰到要将两个没有关系的类组合在一起使用,第一解决方案是:修改各自类的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口。这时候就不能够采用此种方案了,而且修改源码必然的要影响到和这些类相关的类和对象;第二种方式:使用一个适配器,把原来的接口转变为客户所需要的目标接口,在源接口和客户端之间建立一座桥梁,是源接口不用做任何修改就可被目标接口使用。

           适配器模式的目的在于:如果客户需要使用某个类的服务,而这项服务是这个类用一个不同的接口提供的,那么,可以使用适配器为客户提供一个期望的接口。

           客户端使用适配器的具体过程如下:

           第一步:客户端通过目标接口调用适配器的方法对适配器发出请求。

           第二步:适配器使用被适配接口把请求转换成被适配器者的一个或者多个调用接口。

           第三步:客户端收到调用的结果,但是并未觉察到这一切是适配器在起转换作用。

           一般情况下而言,笔者更提倡使用对象适配器,这是因为对象适配器符合良好的面向对象的设计原则,对象适配器使用对象组合,用对客户端适用的接口来包装被适配者,这种做法带来的另外一个优点是使用于被适配这的地方,也适用于被适配者的任何子类;而如果使用类适配器的话,就会丧失这种灵活性,同时类适配是的目标角色不可以是类,因为Java是单继承语言。          

    适配器模式使用场景分析及代码实现: 

           在上面的使用场景中,MM的笔记本电脑的电源插头不可以正常使用,也即是说笔记本的插头和MM学校寝室的接口不相符合,于是GG就到可以市场买了一个适合MM的笔记本和寝室电源接口的转换器,这个转换器插头就是适配器。      

     

    建立一个寝室的电源类:

     1 package com.diermeng.designPattern.Adapter.impl;
     2 
     3 /*
     4 
     5  * 电源
     6 
     7  */
     8 
     9 public class Current {
    10 
    11     /*
    12 
    13      * 电源电压
    14 
    15      */
    16 
    17     public void use220V() {
    18 
    19        System.out.println("寝室电源电压是220V^_^");
    20 
    21     }
    22 
    23 }
    24 
    25  

    建立一个类适配器:

     1 package com.diermeng.designPattern.Adapter.impl;
     2 
     3 /*
     4 
     5  * 类适配器 使用了继承的方式
     6 
     7  */
     8 
     9 public class AdapterClass extends Current{
    10 
    11     /*
    12 
    13      * 客户端期望的接口
    14 
    15      */
    16 
    17     public void use18V() {
    18 
    19        System.out.println("我是类适配器");
    20 
    21        //调用Current的方法
    22 
    23        this.use220V();
    24 
    25     }
    26 
    27 }

    建立类适配器的测试客户端:

     1 package com.diermeng.designPattern.Adapter.client;
     2 
     3 import com.diermeng.designPattern.Adapter.impl.AdapterClass;
     4 
     5  
     6 
     7 /*
     8 
     9  * 类适配器客户端测试
    10 
    11  */
    12 
    13 public class AdapterClassTest {
    14 
    15     public static void main(String[] args) {
    16 
    17        //实例化类适配器
    18 
    19        AdapterClass adapter = new AdapterClass();
    20 
    21        //调用类适配器对客户端提供的方法
    22 
    23        adapter.use18V();
    24 
    25  
    26 
    27     }
    28 
    29 }

    运行结果如下:

    1 我是类适配器
    2 
    3 寝室电源电压是220V^_^

    建立对象适配器:

     1 package com.diermeng.designPattern.Adapter.impl;
     2 
     3 /*
     4 
     5  * 对象适配器
     6 
     7  */
     8 
     9 public class AdapterObject {
    10 
    11     /*
    12 
    13      * 对源目标的引用
    14 
    15      */
    16 
    17     private Current current;
    18 
    19  
    20 
    21     /*
    22 
    23      * 在构造方法中实例化current
    24 
    25      */
    26 
    27     public AdapterObject(Current current) {
    28 
    29        this.current = current;
    30 
    31     }
    32 
    33     /*
    34 
    35      * 对象适配器提供的对客户端期望的接口
    36 
    37      */
    38 
    39     public void use18V() {
    40 
    41        System.out.println("我是对象使用适配器");
    42 
    43        this.current.use220V();
    44 
    45     }
    46 
    47 }

    建立对象适配器的测试客户端:

     1 package com.diermeng.designPattern.Adapter.client;
     2 
     3 import com.diermeng.designPattern.Adapter.impl.AdapterObject;
     4 
     5 import com.diermeng.designPattern.Adapter.impl.Current;
     6 
     7  
     8 
     9 /*
    10 
    11  * 对象适配客户端测试
    12 
    13  */
    14 
    15 public class AdapterObjectTest {
    16 
    17     public static void main(String[] args) {
    18 
    19        //声明并实例化对象适配器
    20 
    21        AdapterObject adapter = new AdapterObject(new Current());
    22 
    23        //调用对象适配器对客户端提供的方法
    24 
    25        adapter.use18V();
    26 
    27  
    28 
    29     }
    30 
    31 }

    对象适配器运行结果:

    1 我是对象使用适配器
    2 
    3 寝室电源电压是220V^_^

    适配器模式的优缺点分析: 

           优点:

           使用适配器模式,能够将一个系统的接口和另外一个系统的接口联系起来,从而使得原本不可以在一起工作的类能够在一起工作,适配器模式强调了对接口的转换。

    缺点:

    对于类适配器而言,不能够适配类的子类;而对于对象适配器而言,重新定义被适配类的行为比较的困难,但是也并非说不可能,这个时候需要使用子类继承的方式来使用修改了被适配类的子类。

    适配器模式的实际应用简介: 

       在大规模的系统开发过程中,我们常常碰到诸如以下这些情况:我们需要实现某些功能,这些功能已有还不太成熟的一个或多个外部组件,如果我们自己重新开发这些功能会花费大量时间;所以很多情况下会选择先暂时使用外部组件,以后再考虑随时替换。但这样一来,会带来一个问题,随着对外部组件库的替换,可能需要对引用该外部组件的源代码进行大面积的修改,因此也极可能引入新的问题等等。如何最大限度的降低修改面呢?Adapter模式就是针对这种类似需求而提出来的。Adapter模式通过定义一个新的接口(对要实现的功能加以抽象),和一个实现该接口的Adapter(适配器)类来透明地调用外部组件。这样替换外部组件时,最多只要修改几个Adapter类就可以了,其他源代码都不会受到影响。

           具体来讲,Adapter模式可应用于如下的情况:

           1、系统需要使用现有的类,而此类的接口不符合系统的需要。

    2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有与一致的接口。这种情况从不同的角度考虑,可能被划入Facade模式的范畴,但从与现有设计适配的角度考虑该问题,则将其划归Adapter模式也是可以理解的。

    3、通过接口转换,将一个类插入另一个类系中。

    温馨提示: 

           一般情况下而言,笔者更提倡使用对象适配器,这是因为对象适配器符合良好的面向对象的设计原则,对象适配器使用对象组合,用对客户端适用的接口来包装被适配者,这种做法带来的另外一个优点是使用于被适配这的地方,也适用于被适配者的任何子类;而如果使用类适配器的话,就会丧失这种灵活性,同时类适配是的目标角色不可以是类,因为Java是单继承语言。  

  • 相关阅读:
    树莓派ZeroW的Python中使用SQLite数据库
    树莓派Python读写配置文件--configparser库
    信号量示例——“生产者——消费者”实验
    互斥锁示例——模拟银行卡取钱
    管道通信(上)
    命名管道——进程通信案例
    文件I/O
    链表习题
    蓝桥杯ACM训练Day4——算法2-8~2-11:链表的基本操作
    C++——类模板几种常见的情况
  • 原文地址:https://www.cnblogs.com/androidxiaoyang/p/3727531.html
Copyright © 2020-2023  润新知