• 设计模式08-适配器模式与桥接模式详解


    1.8设计模式-适配器模式与桥接模式详解

    1.8.1.适配器模式详解

    时长:1h6min

    学习目标
    》掌握适配器和桥接模式的应用场景

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

    》了解模式在源码中应用

    》优缺点

    8.1.1.适配器模式的定义

    定义

      适配器模式,Adapter Pattern,又称变压器模式。它是功能是一个接口变成客户所期望的另一种接口,从而使

    原本因接口不匹配而导致无法在一起工作的两个类能够一起工作。

      属于结构型设计模式。

    生活中适配器模式的应用

    》两脚插转三角插座

    》手机充电接口

    》显示器转接头

    8.1.1.1.应用场景

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

     2.适配器模式不是软件设计阶段考虑的设计模式,而是随着软件维护,由于不同产品,不同厂家造成

    功能类似而接口不相同的情况下的解决方案。

    8.1.2.适配器模式的实现方案

     8.1.2.1.通用写法

      适配器有3个角色,目标角色【期望兼容的角色】,原角色【系统中已存在角色】,适配器

    适配器有3种形式:

      》类适配器

      》对象适配器

      》接口适配器

    1.类适配器写法

    【1】标准实现类图

     2.对象适配器写法
    【1】标准类图

    【2】代码实现

    A.适配原角色

    package com.wf.adapter.general.objectadapter;
    
    /**
     * @ClassName Adaptee
     * @Description 适配原角色
     * @Author wf
     * @Date 2020/6/9 14:47
     * @Version 1.0
     */
    public class Adaptee {
        public int specificRequest(){
            return 220;
        }
    }

    B.目标对象【实现是接口】

    package com.wf.adapter.general.objectadapter;
    
    /**
     * @ClassName Target
     * @Description 目标对象
     * @Author wf
     * @Date 2020/6/9 14:48
     * @Version 1.0
     */
    public interface Target {
        int request();
    }

    C.适配器类

    package com.wf.adapter.general.objectadapter;
    
    /**
     * @ClassName Adapter
     * @Description 对象适配器,组合引用原角色,也得实现目标对象的功能
     * @Author wf
     * @Date 2020/6/9 14:48
     * @Version 1.0
     */
    public class Adapter implements Target {
        //组合引用
        private Adaptee adaptee;
    
        public Adapter(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        @Override
        public int request() {
            return adaptee.specificRequest() / 10;
        }
    }

    D.测试类

    package com.wf.adapter.general.objectadapter;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/9 14:49
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            Target adapter = new Adapter(new Adaptee());
            int result = adapter.request();
            System.out.println("最终结果:"+result);
        }
    }

    说明:

      客户调用时,使用多态的方式。如此,就只会出现接口的功能,而不会出现原角色的功能调用。

     3.接口适配器写法

    它的使用场景:

      它是用于解决,需要适配功能很多的情况,并且都与适配原角色相关,如果针对每一个功能都写一个适配器,就会出现很多的类,

    造成类臃肿。  

      所以,就想到把这多个功能,归类到一个目标对象中,统一创建一个接口适配器,就可以达到一个类适配多个功能的目的。

       解决方案:通过接口适配器,只适配需要的接口方法。

    代码如下所示:

    【1】适配原角色

    package com.wf.adapter.general.interfaceadapter;
    
    /**
     * @ClassName Adaptee
     * @Description 适配原角色
     * @Author wf
     * @Date 2020/6/9 14:47
     * @Version 1.0
     */
    public class Adaptee {
        public int specificRequest(){
            return 220;
        }
    }

    【2】目标对象

    package com.wf.adapter.general.interfaceadapter;
    
    /**
     * @ClassName Target
     * @Description 目标对象
     * @Author wf
     * @Date 2020/6/9 14:48
     * @Version 1.0
     */
    public interface Target {
        int request0();
        int request1();
        int request2();
        int request3();
        int request4();
    }

    说明:

      可以看到,新增目标对象需要很多方法,并且都与适配原角色有关联。

      因此,把这些方法,统一放到一个目标接口中,而不是定义多个目标类。

    【3】适配器类

    package com.wf.adapter.general.interfaceadapter;
    
    /**
     * @ClassName Adapter
     * @Description 接口适配器,组合引用原角色,也得实现目标对象的功能
     * @Author wf
     * @Date 2020/6/9 14:48
     * @Version 1.0
     */
    public class Adapter implements Target {
        //组合引用
        private Adaptee adaptee;
    
        public Adapter(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        @Override
        public int request0() {
            return 0;
        }
    
        @Override
        public int request1() {
            return 0;
        }
    
        @Override
        public int request2() {
            return 0;
        }
    
        @Override
        public int request3() {
            return 0;
        }
    
        @Override
        public int request4() {
            return 0;
        }
    }

    【4】测试类

    package com.wf.adapter.general.interfaceadapter;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/9 14:49
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            Target adapter = new Adapter(new Adaptee());
            int result = adapter.request0();
            System.out.println("最终结果:"+result);
    
            result = adapter.request2();
            System.out.println("最终结果:"+result);
        }
    }
    8.1.2.2.具体实例
    1.类适配器的使用

      这里使用变压器模式来,进行示例说明。

      手机充电器,就是典型的适配器模式。适配器,最初是电工学上一个术语。后来被发展为软件术语。

      手机充电器,获得手机需要的直流电【5v电压】,这可以当作目标对象

      而家电电压是220v交流电,可以视为适配器原角色。

      充电器,把220v交流电压,转换为5v直流电压。就是适配器

    系统实现代码如下:

    【1】适配原角色,220v交流电压

    package com.wf.adapter.demo.classadapter;
    
    /**
     * @ClassName AC220
     * @Description 交流电压220V
     * @Author wf
     * @Date 2020/6/3 14:36
     * @Version 1.0
     */
    public class AC220 {
        public int outputAC220V(){
            int output = 220;
            System.out.println("输出电压:"+output+" V");
            return output;
        }
    }

    【2】定义目标对象【实际是接口】

    package com.wf.adapter.demo.classadapter;
    
    /**
     * @ClassName DC5
     * @Description 直流电压5v
     * @Author wf
     * @Date 2020/6/3 14:39
     * @Version 1.0
     */
    public interface DC5 {
        int output5V();
    }

    【3】充电器,适配器

    package com.wf.adapter.demo.classadapter;
    
    /**
     * @ClassName PowerAdapter
     * @Description 电源适配器
     * @Author wf
     * @Date 2020/6/3 14:40
     * @Version 1.0
     */
    public class PowerAdapter extends AC220 implements DC5 {
        @Override
        public int output5V() {
            int adapterInput = super.outputAC220V();
            int adapterOutput = adapterInput / 44;
            System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V");
            return adapterOutput;
        }
    }

     说明:

      适配器类中,继承适配原角色,实现目标接口

      实现接口的功能,调用原角色的功能,并进行扩展。

    【4】测试类

    package com.wf.adapter.demo.classadapter;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/3 14:44
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            PowerAdapter adapter = new PowerAdapter();
            adapter.output5V();
        }
    }

    测试结果如下:

     说明:

      由于适配器,继承原角色,并实现目标接口。所以,适配器具有两者的功能。

      显然,有些违背最少知道原则。

      

      因此,提出对象适配器写法。它的实现思路:

    弃用继承结构,而采用组合方式

    2.对象适配器写法,改写系统
    【1】适配原角色

    不需要修改,如下所示:

    package com.wf.adapter.demo.objectadapter;
    
    /**
     * @ClassName AC220
     * @Description 交流电压220V
     * @Author wf
     * @Date 2020/6/3 14:36
     * @Version 1.0
     */
    public class AC220 {
        public int outputAC220V(){
            int output = 220;
            System.out.println("输出电压:"+output+" V");
            return output;
        }
    }
    【2】目标对象

    代码逻辑不变,如下所示:

    package com.wf.adapter.demo.objectadapter;
    
    /**
     * @ClassName DC5
     * @Description 直流电压5v
     * @Author wf
     * @Date 2020/6/3 14:39
     * @Version 1.0
     */
    public interface DC5 {
        int output5V();
    }

     【3】适配器类

    package com.wf.adapter.demo.objectadapter;
    
    /**
     * @ClassName PowerAdapter
     * @Description 电源适配器
     * @Author wf
     * @Date 2020/6/3 14:40
     * @Version 1.0
     */
    public class PowerAdapter implements DC5 {
        private AC220 ac220;
    
        public PowerAdapter(AC220 ac220) {
            this.ac220 = ac220;
        }
    
        @Override
        public int output5V() {
            int adapterInput = ac220.outputAC220V();
            int adapterOutput = adapterInput / 44;
            System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V");
            return adapterOutput;
        }
    }

    说明:

      采用组合引用,来代替继承方式。

    【4】测试类
    package com.wf.adapter.demo.objectadapter;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/3 14:44
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            DC5 adapter = new PowerAdapter(new AC220());
            adapter.output5V();
        }
    }
    3.接口适配器案例

      针对上面的系统,适配器需要转换多种电压,如:5v,12v,24v...

      只需要把这多个功能,统一归集到一个目标类,然后一个适配器就可以实现。

    【1】适配原角色
    package com.wf.adapter.demo.interfaceadapter;
    
    /**
     * @ClassName AC220
     * @Description 交流电压220V
     * @Author wf
     * @Date 2020/6/3 14:36
     * @Version 1.0
     */
    public class AC220 {
        public int outputAC220V(){
            int output = 220;
            System.out.println("输出电压:"+output+" V");
            return output;
        }
    }
    【2】目标对象

    内部提供多个功能:

    package com.wf.adapter.demo.interfaceadapter;
    
    /**
     * @ClassName DC
     * @Description 目标类,实现多个转换功能
     * @Author wf
     * @Date 2020/6/9 16:04
     * @Version 1.0
     */
    public interface DC {
        int output5V();
        int output12V();
        int output24V();
        int output36V();
    }
    【3】适配器
    package com.wf.adapter.demo.interfaceadapter;
    
    import com.wf.adapter.demo.objectadapter.AC220;
    import com.wf.adapter.demo.objectadapter.DC5;
    
    /**
     * @ClassName PowerAdapter
     * @Description 电源适配器
     * @Author wf
     * @Date 2020/6/3 14:40
     * @Version 1.0
     */
    public class PowerAdapter implements DC {
        private AC220 ac220;
    
        public PowerAdapter(AC220 ac220) {
            this.ac220 = ac220;
        }
    
        @Override
        public int output5V() {
            int adapterInput = ac220.outputAC220V();
            int adapterOutput = adapterInput / 44;
            System.out.println("使用Adapter输入AC"+adapterInput + "V,输出DC["+adapterOutput +"]V");
            return adapterOutput;
        }
    
        @Override
        public int output12V() {
            return 0;
        }
    
        @Override
        public int output24V() {
            return 0;
        }
    
        @Override
        public int output36V() {
            return 0;
        }
    }
    【4】测试类
    package com.wf.adapter.demo.interfaceadapter;
    
    import com.wf.adapter.demo.objectadapter.AC220;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/9 16:12
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            DC adapter = new PowerAdapter(new AC220());
            adapter.output5V();
        }
    }
     8.1.2.3.开发中适配器模式应用案例
    1.系统需求

      是一个管理平台登录场景。在大量的老系统中,需要用户注册,然后登录,才能使用平台。

    但是,当大量用户使用时,这种帐号登录的方式,管理起来反而很复杂。

      所以,从而使用员工的qq,或微信号进行登录,从而免去注册的过程。

      然而,系统的注册,登录功能仍然不能修改,这时就可以使用适配器模式,来解决系统兼容性问题。

    2.代码实现

    【1】原有登录系统功能

    A.业务类,员工类

    package com.wf.adapter.demo.passport;
    
    /**
     * @ClassName Member
     * @Description 成员类
     * @Author wf
     * @Date 2020/6/9 16:18
     * @Version 1.0
     */
    public class Member {
        private String username;
        private String password;
        private String mid;
        private String info;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getMid() {
            return mid;
        }
    
        public void setMid(String mid) {
            this.mid = mid;
        }
    
        public String getInfo() {
            return info;
        }
    
        public void setInfo(String info) {
            this.info = info;
        }
    }

    B.结果集pojo

    package com.wf.adapter.demo.passport;
    
    /**
     * @ClassName ResultMsg
     * @Description 结果集
     * @Author wf
     * @Date 2020/6/9 16:19
     * @Version 1.0
     */
    public class ResultMsg {
        private int code;
        private String msg;
        private Object data;
        public ResultMsg(int code, String msg,String data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    }

    C.登录注册服务

    package com.wf.adapter.demo.passport;
    
    /**
     * @ClassName PassportService
     * @Description 登录,注册服务
     * @Author wf
     * @Date 2020/6/9 16:18
     * @Version 1.0
     */
    public class PassportService {
        public ResultMsg register(String username,String password){
            return new ResultMsg(200,"注册成功",null);
        }
        public ResultMsg login(String username,String password){
            return null;
        }
    }

    【2】适配器扩展系统 

    A.目标类【定义为接口】

    这里需要实现第三方登录,如:qq登录。就需要使用腾训提供open api【需要传参openId】,如下所示:

    package com.wf.adapter.demo.passport;
    
    /**
     * @ClassName IPassortForThird
     * @Description 第三方登录,目标类
     * @Author wf
     * @Date 2020/6/10 10:06
     * @Version 1.0
     */
    public interface IPassportForThird {
        /**
         * qq登录
         * @param openId
         * @return
         */
        ResultMsg loginForQQ(String openId);
    
        /**
         * 微信号登录
         * @param openId
         * @return
         */
        ResultMsg loginForWeChat(String openId);
    
        /**
         * 内部员工token登录
         * @param token
         * @return
         */
        ResultMsg loginForToken(String token);
    
        /**
         * 手机号+验证码登录
         * @param phone
         * @param code
         * @return
         */
        ResultMsg loginForTelephone(String phone, String code);
    }

    B.适配器类

      

      这里使用,类适配器实现。

    package com.wf.adapter.demo.passport;
    
    /**
     * @ClassName PassportForThirdAdapter
     * @Description 第三方登录适配器,类适配器
     * @Author wf
     * @Date 2020/6/10 10:15
     * @Version 1.0
     */
    public class PassportForThirdAdapter extends PassportService implements IPassportForThird {
        @Override
        public ResultMsg loginForQQ(String openId) {
            return loginForRegister(openId,null);
        }
    
        @Override
        public ResultMsg loginForWeChat(String openId) {
            return loginForRegister(openId,null);
        }
    
        @Override
        public ResultMsg loginForToken(String token) {
            return loginForRegister(token,null);
        }
    
        @Override
        public ResultMsg loginForTelephone(String phone, String code) {
            return loginForRegister(phone,null);
        }
    
        /**
         * 提供一个老的登录逻辑,先注册,再登录
         * 当使用第三方登录时,不能够得到密码,处理办法是:如果密码为null,约定一个特殊密码。
         * 后台做一个标识,只要得到这个特殊密码,都表示第三方登录,走特殊流程
         * @param username
         * @param password
         * @return
         */
        private ResultMsg loginForRegister(String username, String password){
            if(null == password){
                password = "THIRD_EMPTY";
            }
            super.register(username,password);
            return super.login(username,password);
        }
    }

    3.测试类

    package com.wf.adapter.demo.passport;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/10 10:25
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            IPassportForThird service = new PassportForThirdAdapter();
            //给一个openId,不会是qq号,为了保护用户隐私
            service.loginForQQ("owpgnw");
        }
    }

     

    说明:

      功能基本完成。但是,由于适配器内部需要进行各种openAPI接入,导致类的职责过重

    而且,当系统进行需求扩展时,如:需要支持抖音帐号登录。就需要修改适配器原有代码,不符合开闭原则。

    4.扩展需求,增加抖音帐号登录

    由于功能扩展,原来的适配器类,可能职责过重,依赖大量openAPI.

    因此,可以对系统进行拆分,针对每一种登录,实现一个适配器,来处理登录逻辑。

    【1】目标类【实为接口】

    package com.wf.adapter.demo.passport.v2;
    
    import com.wf.adapter.demo.passport.ResultMsg;
    
    /**
     * @ClassName IPassportForThird
     * @Description 第三方登录,目标类
     * @Author wf
     * @Date 2020/6/10 10:06
     * @Version 1.0
     */
    public interface IPassportForThird {
        /**
         * qq登录
         * @param openId
         * @return
         */
        ResultMsg loginForQQ(String openId);
    
        /**
         * 微信号登录
         * @param openId
         * @return
         */
        ResultMsg loginForWeChat(String openId);
    
        /**
         * 内部员工token登录
         * @param token
         * @return
         */
        ResultMsg loginForToken(String token);
    
        /**
         * 手机号+验证码登录
         * @param phone
         * @param code
         * @return
         */
        ResultMsg loginForTelephone(String phone, String code);
    }

    【2】适配器中转类

    package com.wf.adapter.demo.passport.v2;
    
    import com.wf.adapter.demo.passport.PassportService;
    import com.wf.adapter.demo.passport.ResultMsg;
    
    /**
     * @ClassName PassportForThirdAdapter
     * @Description 第三方登录适配,中间实现类
     * @Author wf
     * @Date 2020/6/10 10:15
     * @Version 1.0
     */
    public class PassportForThirdAdapter implements IPassportForThird {
        @Override
        public ResultMsg loginForQQ(String openId) {
            return processLogin(openId,LoginForQQAdapter.class);
        }
    
        @Override
        public ResultMsg loginForWeChat(String openId) {
            return processLogin(openId,LoginForWeChatAdapter.class);
        }
    
        @Override
        public ResultMsg loginForToken(String token) {
            return processLogin(token,LoginForTokenAdapter.class);
        }
    
        @Override
        public ResultMsg loginForTelephone(String phone, String code) {
            return processLogin(phone,LoginForTelephoneAdapter.class);
        }
    
        private ResultMsg processLogin(String id, Class<? extends ILoginAdapter> clazz){
            try {
                ILoginAdapter adapter = clazz.newInstance();
                if(adapter.support(adapter)){
                    return adapter.login(id,adapter);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    
    }

    【3】顶层适配器接口

    package com.wf.adapter.demo.passport.v2;
    
    import com.wf.adapter.demo.passport.ResultMsg;
    
    /**
     * @ClassName ILoginAdapter
     * @Description 适配器公共接口
     * @Author wf
     * @Date 2020/6/10 10:36
     * @Version 1.0
     */
    public interface ILoginAdapter {
        /**
         * 匹配适配器子类实现
         * @param object
         * @return
         */
        boolean support(Object object);
    
        /**
         * 登录逻辑
         * @param id
         * @param adapter
         * @return
         */
        ResultMsg login(String id,Object adapter);
    }

    【4】单个适配器实现

    package com.wf.adapter.demo.passport.v2;
    
    
    /**
     * @ClassName LoginForQQAdapter
     * @Description 适配器
     * @Author wf
     * @Date 2020/6/10 10:45
     * @Version 1.0
     */
    public class LoginForQQAdapter extends AbstractAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForQQAdapter;
        }
    
    }
    package com.wf.adapter.demo.passport.v2;
    
    import com.wf.adapter.demo.passport.ResultMsg;
    
    /**
     * @ClassName LoginForTelephoneAdapter
     * @Description TODO
     * @Author wf
     * @Date 2020/6/10 14:02
     * @Version 1.0
     */
    public class LoginForTelephoneAdapter extends AbstractAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForTelephoneAdapter;
        }
    }
    
    package com.wf.adapter.demo.passport.v2;
    
    /**
     * @ClassName LoginForTokenAdapter
     * @Description 内部员工登录,适配器
     * @Author wf
     * @Date 2020/6/10 14:00
     * @Version 1.0
     */
    public class LoginForTokenAdapter extends AbstractAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForTokenAdapter;
        }
    }
    
    package com.wf.adapter.demo.passport.v2;
    
    /**
     * @ClassName loginForWeChatAdapter
     * @Description 第三方登录,微信登录
     * @Author wf
     * @Date 2020/6/10 13:57
     * @Version 1.0
     */
    public class LoginForWeChatAdapter extends AbstractAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForWeChatAdapter;
        }
    }

    【5】抽取适配器公共实现

    package com.wf.adapter.demo.passport.v2;
    
    import com.wf.adapter.demo.passport.PassportService;
    import com.wf.adapter.demo.passport.ResultMsg;
    
    /**
     * @ClassName AbstractAdapter
     * @Description 提取公共代码
     * @Author wf
     * @Date 2020/6/10 10:52
     * @Version 1.0
     */
    public abstract class AbstractAdapter extends PassportService implements ILoginAdapter{
    
        /**
         * 提供一个老的登录逻辑,先注册,再登录
         * 当使用第三方登录时,不能够得到密码,处理办法是:如果密码为null,约定一个特殊密码。
         * 后台做一个标识,只要得到这个特殊密码,都表示第三方登录,走特殊流程
         * @param username
         * @param password
         * @return
         */
        protected ResultMsg loginForRegister(String username, String password){
            if(null == password){
                password = "THIRD_EMPTY";
            }
            super.register(username,password);
            return super.login(username,password);
        }
    
        @Override
        public ResultMsg login(String id, Object adapter) {
            return loginForRegister(id,null);
        }
    }

    【6】测试类

    package com.wf.adapter.demo.passport.v2;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/10 14:05
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            IPassportForThird adapter = new PassportForThirdAdapter();
            adapter.loginForQQ("qpgqjgpwokoekfkfod");
        }
    }

    说明:

      如果要扩展抖音帐号登录,需要修改目标接口及实现。并且增加一个适配器实现子类。

      这只能在一定程度上,实现动态扩展的目的。但并不能够真正地满足开闭原则。

    【7】系统类图

     2.1.3.适配器模式在源码中应用

    2.1.3.1.spring中aop模块
    public interface AdvisorAdapter {
        boolean supportsAdvice(Advice var1);
    
        MethodInterceptor getInterceptor(Advisor var1);
    }

    实现类如下:

    class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
        MethodBeforeAdviceAdapter() {
        }
    
        public boolean supportsAdvice(Advice advice) {
            return advice instanceof MethodBeforeAdvice;
        }
    
        public MethodInterceptor getInterceptor(Advisor advisor) {
            MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
            return new MethodBeforeAdviceInterceptor(advice);
        }
    }
    2.1.3.2.spring中mvc模块
    public interface HandlerAdapter {
        boolean supports(Object var1);
    
        @Nullable
        ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    
        long getLastModified(HttpServletRequest var1, Object var2);
    }

    适配器各实现子类:

     实现子类:

    public class SimpleControllerHandlerAdapter implements HandlerAdapter {
        public SimpleControllerHandlerAdapter() {
        }
    
        public boolean supports(Object handler) {
            return handler instanceof Controller;
        }
    
        @Nullable
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            return ((Controller)handler).handleRequest(request, response);
        }
    
        public long getLastModified(HttpServletRequest request, Object handler) {
            return handler instanceof LastModified ? ((LastModified)handler).getLastModified(request) : -1L;
        }
    }

    适配器的使用:

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
            if (this.handlerAdapters != null) {
                Iterator var2 = this.handlerAdapters.iterator();
    
                while(var2.hasNext()) {
                    HandlerAdapter ha = (HandlerAdapter)var2.next();
                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace("Testing handler adapter [" + ha + "]");
                    }
    
                    if (ha.supports(handler)) {  //判断使用哪一种适配器
                        return ha;
                    }
                }
            }
    
            throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
        }

    2.1.4.适配器模式总结

    2.1.4.1.优点,缺点总结

    优点:

      能提高类的透明性和复用性,现有的类复用且不需要改变。

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

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

    缺点:

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

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

    1.8.2.桥接模式详解

    时长:58min

    8.2.1.桥接模式定义

    定义

      Bridge Pattern,也称桥梁模式,接口模式,或柄体(Handle and Body)模式,是将抽象部分与它的具体

    实现部分分离,使它们可以独立变化的。

      通过组合的方式建立两个类之间的联系,而不使用继承。

      属于结构型模式。

    桥接模式在生活中的应用场景

    1.连接两个空间的拱桥

    2.虚拟网络与真实网络的桥接

    8.2.1.1.适用场景

    1.在抽象和具体实现之间需要增加更多的灵活性的场景

    2.一个类存在两个(或多个)独立变化的维度,而这两个(或多个)维度都需要独立进行扩展

    3.不希望继承,或因为多层继承导致系统类的个数剧增【多用组合引用】

    8.2.2.桥接模式的写法

    8.2.2.1.通用写法
    1.系统类图设计

     2.代码实现

    A.顶层接口

    package com.wf.bridge.general;
    
    /**
     * @ClassName IImplementor
     * @Description 顶层接口定义
     * @Author wf
     * @Date 2020/6/10 15:28
     * @Version 1.0
     */
    public interface IImplementor {
        void operationImpl();
    }

    B.接口实现

    package com.wf.bridge.general;
    
    /**
     * @ClassName ConcreteImplementorA
     * @Description 实现
     * @Author wf
     * @Date 2020/6/10 15:29
     * @Version 1.0
     */
    public class ConcreteImplementorA implements IImplementor {
        @Override
        public void operationImpl() {
            System.out.println("I'm ConcreteImplementorA");
        }
    }
    package com.wf.bridge.general;
    
    /**
     * @ClassName ConcreteImplementorB
     * @Description 接口实现B
     * @Author wf
     * @Date 2020/6/10 15:29
     * @Version 1.0
     */
    public class ConcreteImplementorB implements IImplementor {
        @Override
        public void operationImpl() {
            System.out.println("I'm ConcreteImplementorB");
        }
    }

    C.抽象类组合引用接口实例

    package com.wf.bridge.general;
    
    /**
     * @ClassName Abstraction
     * @Description 抽象类,组合引用接口实例
     * @Author wf
     * @Date 2020/6/10 15:27
     * @Version 1.0
     */
    public abstract class Abstraction{
        protected IImplementor mImplementor;
    
        public Abstraction(IImplementor mImplementor) {
            this.mImplementor = mImplementor;
        }
    
        public void operation(){
            this.mImplementor.operationImpl();
        }
    }

    说明:

      这里很像是包装器模式。

    D.修改抽象实现

    package com.wf.bridge.general;
    
    /**
     * @ClassName RefinedAbstraction
     * @Description 修正抽象
     * @Author wf
     * @Date 2020/6/10 15:31
     * @Version 1.0
     */
    public class RefinedAbstraction extends Abstraction {
    
        public RefinedAbstraction(IImplementor mImplementor) {
            super(mImplementor);
        }
    
        @Override
        public void operation() {
            super.operation();
            System.out.println("refined operation");
        }
    }

    E.测试类

    package com.wf.bridge.general;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/10 15:32
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
            RefinedAbstraction abstraction = new RefinedAbstraction(new ConcreteImplementorA());
            abstraction.operation();
        }
    }
    8.2.2.2.桥接模式具体案例应用
    1.需求分析

      在前面的抽象工厂模式学习中,涉及产品簇和产品等级的实现案例。

      产品簇,可以理解为产品的实现维度

      产品等级,理解为产品的抽象维度。  

      从而,可以使用桥接模式,将两种关系联系起来。

    具体需求:

      针对学习课程Course,课程内容有:笔记,源码,ppt,课后作业【理解为产品抽象

      不同类型的课程:java,Python,AI,大数据。。。【理解为产品实现

    2.代码实现

    【1】定义产品实现顶层接口--课程

    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName ICourse
     * @Description 课程,顶层接口定义
     * @Author wf
     * @Date 2020/6/10 15:58
     * @Version 1.0
     */
    public interface ICourse {
    }

    实现类:

    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName JavaCourse
     * @Description java课程
     * @Author wf
     * @Date 2020/6/10 16:00
     * @Version 1.0
     */
    public class JavaCourse implements ICourse {
    
    }
    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName PythonCourse
     * @Description Python课程
     * @Author wf
     * @Date 2020/6/10 16:01
     * @Version 1.0
     */
    public class PythonCourse implements ICourse {
    }

    【2】增加第二个维度的接口及实现----笔记

    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName INote
     * @Description 笔记接口
     * @Author wf
     * @Date 2020/6/10 16:02
     * @Version 1.0
     */
    public interface INote {
        void edit();
    }
    
    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName JavaNote
     * @Description java课程笔记
     * @Author wf
     * @Date 2020/6/10 16:03
     * @Version 1.0
     */
    public class JavaNote implements INote {
        @Override
        public void edit() {
    
        }
    }
    
    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName PythonNote
     * @Description Python笔记
     * @Author wf
     * @Date 2020/6/10 16:03
     * @Version 1.0
     */
    public class PythonNote implements INote {
        @Override
        public void edit() {
    
        }
    }

    【3】增加第三个维度的接口及实现----视频

    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName IVideo
     * @Description 视频接口
     * @Author wf
     * @Date 2020/6/10 16:04
     * @Version 1.0
     */
    public interface IVideo {
    }
    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName JavaVideo
     * @Description TODO
     * @Author wf
     * @Date 2020/6/10 16:05
     * @Version 1.0
     */
    public class JavaVideo implements IVideo {
    }
    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName PythonVideo
     * @Description TODO
     * @Author wf
     * @Date 2020/6/10 16:05
     * @Version 1.0
     */
    public class PythonVideo implements IVideo {
    }

    说明:

      现在,产品的三个维度是相互独立的,互不相关。

      想要它们关联起来,因此,使用桥接模式,创建抽象,引用接口实例。、

    【4】创建抽象,引用接口实现

    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName AbstractCourse
     * @Description 课程抽象
     * @Author wf
     * @Date 2020/6/10 16:14
     * @Version 1.0
     */
    public class AbstractCourse implements ICourse {
        private IVideo video;
        private INote note;
    
        public void setVideo(IVideo video) {
            this.video = video;
        }
    
        public void setNote(INote note) {
            this.note = note;
        }
    
        @Override
        public String toString() {
            return "AbstractCourse{" +
                    "video=" + video +
                    ", note=" + note +
                    '}';
        }
    }

    说明:

      因为Course存在不同实现,当提供这样一个抽象之后,原来的课程实现,就需要修改。

      不是实现接口,而是直接继承这个抽象,修改后如下所示:

    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName JavaCourse
     * @Description java课程
     * @Author wf
     * @Date 2020/6/10 16:00
     * @Version 1.0
     */
    public class JavaCourse extends AbstractCourse{
    
    }
    package com.wf.bridge.demo.course;
    
    /**
     * @ClassName PythonCourse
     * @Description Python课程
     * @Author wf
     * @Date 2020/6/10 16:01
     * @Version 1.0
     */
    public class PythonCourse extends AbstractCourse {
    }

    此时,系统类图所示:

    8.2.2.3.桥接模式具体案例实现二
    1.系统需求分析

      在我们日常的办公过程中,存在大量的消息同步,发送问题。

      发送消息,可能采取不同的方式,如:邮件,短信,系统内消息

      消息的类型,有不同:普通消息,加急消息,特急消息。

    如下图示:

     2.代码实现

    【1】定义消息顶层接口

    package com.wf.bridge.demo.message;
    
    /**
     * @ClassName IMessage
     * @Description 顶层消息接口
     * @Author wf
     * @Date 2020/6/10 16:33
     * @Version 1.0
     */
    public interface IMessage {
        void send(String content, String user);
    }

    【2】消息发送方式子类实现

    package com.wf.bridge.demo.message;
    
    /**
     * @ClassName EmailMessage
     * @Description 邮件方式发消息
     * @Author wf
     * @Date 2020/6/10 16:35
     * @Version 1.0
     */
    public class EmailMessage implements IMessage {
        @Override
        public void send(String content,String user) {
            System.out.println("使用邮件发送消息【"+content+"】,给【"+user+"】");
        }
    }
    ackage com.wf.bridge.demo.message;
    
    /**
     * @ClassName SmsMessage
     * @Description 短信方式发送消息
     * @Author wf
     * @Date 2020/6/10 16:40
     * @Version 1.0
     */
    public class SmsMessage implements IMessage{
        @Override
        public void send(String content, String user) {
            System.out.println("使用短信发送消息【"+content+"】,给【"+user+"】");
        }
    }

    【3】抽象引用消息实现

    package com.wf.bridge.demo.message;
    
    /**
     * @ClassName AbstractMessage
     * @Description 消息类型,抽象
     * @Author wf
     * @Date 2020/6/10 16:44
     * @Version 1.0
     */
    public abstract class AbstractMessage {
        private IMessage message;
    
        public AbstractMessage(IMessage message) {
            this.message = message;
        }
        public void send(String content, String toUser){
            message.send(content,toUser);
        }
    }

     【4】抽象消息修正

    package com.wf.bridge.demo.message;
    
    /**
     * @ClassName NormalMessage
     * @Description 普通消息
     * @Author wf
     * @Date 2020/6/10 16:50
     * @Version 1.0
     */
    public class NormalMessage extends AbstractMessage {
        public NormalMessage(IMessage message) {
            super(message);
        }
    }
    
    package com.wf.bridge.demo.message;
    
    import javax.sound.midi.Soundbank;
    
    /**
     * @ClassName UrgencyMessage
     * @Description 加急消息
     * @Author wf
     * @Date 2020/6/10 16:47
     * @Version 1.0
     */
    public class UrgencyMessage extends AbstractMessage {
        public UrgencyMessage(IMessage message) {
            super(message);
        }
    
        @Override
        public void send(String content, String toUser) {
            content += "[加急]";
            super.send(content, toUser);
            watch();
        }
        public void watch(){
            System.out.println("还需要做进度跟踪");
        }
    }

    【5】测试类

    package com.wf.bridge.demo.message;
    
    /**
     * @ClassName Test
     * @Description 测试类
     * @Author wf
     * @Date 2020/6/10 16:51
     * @Version 1.0
     */
    public class Test {
        public static void main(String[] args) {
           IMessage message = new SmsMessage();
           AbstractMessage abstractMessage = new NormalMessage(message);
           abstractMessage.send("加班申请","王总");
    
           //申请一直没回,比较着急了,需要加急
            message = new EmailMessage();
            abstractMessage = new UrgencyMessage(message);
            abstractMessage.send("加班申请","王总");
        }
    }

    测试结果如下:

     【6】系统类图

     8.2.3.桥接模式在源码中应用

    8.2.3.1.jdbc连接

      java中连接数据库,底层通过jdbc来实现。

      java只是提供了数据库连接的抽象,具体实现由数据库厂商来实现【如:mysql,oracle...】

      所以,这里就用到了桥接模式。

    1.测试类
    package com.wf.bridge.demo.jdbc;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    /**
     * @ClassName Test
     * @Description TODO
     * @Author wf
     * @Date 2020/6/10 17:03
     * @Version 1.0
     */
    @Slf4j
    public class Test {
        public static void main(String[] args) {
            try {
                //1.加载驱动
                Class.forName("com.mysql.jdbc.Driver");
                //2.获取连接Connection实例
                //主机号:端口号/数据库名
                Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test3","root","root");
                Statement stmt = connection.createStatement();
                ResultSet rs = stmt.executeQuery("select * from dsg_red_list_info");
                System.out.println(rs);
    
            }catch (Exception e){
                log.error("数据库操作失败:{}",e.getMessage());
            }
        }
    }
    2.桥接模式应用
    package com.mysql.jdbc;
    
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    public class Driver extends NonRegisteringDriver implements java.sql.Driver {
        public Driver() throws SQLException {
        }
    
        static {
            try {
                DriverManager.registerDriver(new Driver());
            } catch (SQLException var1) {
                throw new RuntimeException("Can't register driver!");
            }
        }
    }

    为什么要实现这个java.sql.Driver呢?

    在测试类,通过Class.forName找到Driver之后,首先,会加载静态块。

    由DriverManager封装mysql的Driver实例,并存储到list中。

    第二步,由DriverManager获取Connection实例。

    DriverManage起到桥梁作用。将java的Driver,Connection和数据库厂商建立建立连接。

    8.2.4.桥接模式的总结

    8.2.4.1.优,缺点总结

    优点:

      》分离抽象部分及其具体实现部分

      》提高了系统的扩展性

      》符合开闭原则

      》符合合成复用原则

    缺点:

      1.增加系统的理解与设计难度

      2.需要正确地识别系统中两个独立变化的维度

    8.2.4.2.桥接模式相关设计模式
    1.桥接模式与组合模式

    桥接模式:关注组合引用

    组合模式:关注共同功能,根据主线组合

    2.桥接模式与适配器模式

    适配器:根据目标类,进行扩展,提高灵活性

    桥接模式:必须有约定前提,并且严格按照约定实现。

  • 相关阅读:
    手机处理器之雄霸天下
    android Bitmap总结
    所谓编程
    Android在Eclipse下编译String.xml出现Multiple substitutions specified in nonpositional format 错误
    关于“求余”运算的一些小感想
    QWrap简介之:Helper规范
    QWrap简介之:瘦主干
    QWrap简介之:HelperH 针对helper的Helper
    QWrap代码规范化经历
    QWrap简介之:设计主线
  • 原文地址:https://www.cnblogs.com/wfdespace/p/13026710.html
Copyright © 2020-2023  润新知