• 设计模式(五)之适配器模式(Adapter Pattern)深入浅出


    学习目标:通过学习适配器模式,优雅地解决代码功能的兼容性问题。

    适配器模式的定义:

    • 适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作。
    • 属于结构型模式。

    适配器模式的适用场景:

    • 已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
    • 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。

    适配器源码案例:

    • SpringAOP源码中的AdvisorAdapter接口

     它的具体实现有AfterReturningAdviceAdapter(方法返回时通知)、MethodBeforeAdviceAdapter(方法调用前通知)、ThrowsAdviceAdapter(方法出现异常通知),spring根据配置需要调用需要的通知,也可以时三个,也可以是两个;而策略模式只能选择一种逻辑,不能并行

    类结构图

    •  SpringMvc的HandlerAdapter接口

     类结构图

    生活场景案例

    案例:变压器将家用220v交流电转换成5v直流电

    5v直流电接口

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:39
     * @description: 5V直流电
     */
    public interface DC5 {
    
        int outputDC5V();
    }

    220v交流电

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:37
     * @description: 220V交流电
     */
    public class AC220 {
    
        public int outputAC220V(){
            int output = 220;
            System.out.println("输出电流" + output + "V");
            return output;
        }
    }

    变压器

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:41
     * @description:
     */
    public class PowerAdapter implements DC5 {
    
        private AC220 ac220;
    
        public PowerAdapter(AC220 ac220) {
            this.ac220 = ac220;
        }
    
        @Override
        public int outputDC5V() {
            int adapterInput = ac220.outputAC220V();
            int adapterOutput = adapterInput / 44;
            System.out.println("使用PowerAdapter输入AC:" + adapterInput + "V,输出DC:" + adapterOutput + "V");
            return adapterOutput;
        }
    }

    测试

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:45
     * @description:
     */
    public class PowerAdapterTest {
    
        public static void main(String[] args) {
            DC5 dc5 = new PowerAdapter(new AC220());
            dc5.outputDC5V();
        }
    }

    业务场景案例

    案例:系统需要在原有的登录方式上,添加QQ、微信等多种登录方式

    返回对象ResultMsg

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:55
     * @description:
     */
    public class ResultMsg {
    
        private int code;
        private String msg;
        private String data;
    
        public ResultMsg(int code, String msg, String data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    }

    已经存在的登录注册逻辑

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:51
     * @description: 原有的登录注册逻辑
     */
    public class SinginService {
    
        /**
         * 注册方法
         */
        public ResultMsg regist(String username,String password){
            return new ResultMsg(200,"注册成功",null);
        }
    
        /**
         * 登录方法
         */
        public ResultMsg login(String username,String password){
            return new ResultMsg(200,"登录成功",null);
        }
    }

    方式一:直接继承原有登录注册服务

    SinginForThirdService.java
    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:58
     * @description:
     */
    public class SinginForThirdService extends SinginService {
    
        public ResultMsg loginForQQ(String openId) {
    //        1、openId是全局唯一,我们可以把它当做是一个用户名(加长)
    //        2、密码默认为QQ_EMPTY
    //        3、注册(在原有系统里面创建一个用户)
    //        4、调用原来的登录方法
            return loginForRegist(openId,null);
    
        }
    
        public ResultMsg loginForWechat(String openId){
            return null;
        }
        public ResultMsg loginForToken(String token){
    //        通过token拿到用户信息,然后再重新登录了一次
            return null;
        }
        public ResultMsg loginForTelphone(String telphone,String code){
            return null;
        }
    
    
        /**
         * qq登录自动注册
         */
        public ResultMsg loginForRegist(String username,String password){
            super.regist(username,null);
            return super.login(username,null);
        }
    
    
    }

    测试

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:11
     * @description:
     */
    public class SinginForThirdServiceTest {
        public static void main(String[] args) {
            SinginForThirdService singinForThirdService = new SinginForThirdService();
            singinForThirdService.login("zc","123456");
            singinForThirdService.loginForQQ("fadfafd");
            singinForThirdService.loginForWechat("fdsaf");
        }
    }

    方式二:利用适配器模式

    Spring中适配器的实现方式,完全模仿SpringAOP的AdivsorAdapter、Spring Web Mvc中的HandlerAdapter实现

    业务扩展接口

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:13
     * @description: 只扩展的接口
     */
    public interface IPassportForThird {
    
        /**
         * QQ登录
         */
        ResultMsg loginForQQ(String openId);
    
        /**
         * 微信登录
         */
        ResultMsg loginForWechat(String openId);
    
        /**
         * 新浪登录
         */
        ResultMsg loginForSina(String openId);
    
        /**
         * token登录
         */
        ResultMsg loginForToken(String token);
    
        /**
         * 手机验证登录
         */
        ResultMsg loginForTelphone(String telphone,String code);
    
        /**
         * 注册账号后自动登录
         */
        ResultMsg loginForRegister(String username,String passport);
    
    
    }

    适配器接口(可有可无)

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:20
     * @description:    在适配器里面这个接口是可无可有的,不要和模板模式混淆,
     *                  模板模式一定是抽象类,而这里面只是一个接口
     */
    public interface LoginAdapter {
    
        /**
         * 适配器兼容性判断
         */
        boolean support(Object adapter);
        /**
         * 登录逻辑
         */
        ResultMsg login(String id,Object a);
    }

    QQ登录

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:23
     * @description: QQ登录
     */
    public class LoginForQQAdapter implements LoginAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForQQAdapter;
        }
    
        @Override
        public ResultMsg login(String id, Object a) {
    //        具体逻辑
            return null;
        }
    }

    新浪登录

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:26
     * @description: 新浪登录
     */
    public class LoginForSinaAdapter implements LoginAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForSinaAdapter;
        }
    
        @Override
        public ResultMsg login(String id, Object a) {
            return null;
        }
    }

    手机短信登录

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:28
     * @description: 手机短信登录
     */
    public class LoginForTelAdapter implements LoginAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForTelAdapter;
        }
    
        @Override
        public ResultMsg login(String id, Object a) {
            return null;
        }
    }

    token令牌登录

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:29
     * @description: token登录
     */
    public class LoginForTokenAdapter implements LoginAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForTokenAdapter;
        }
    
        @Override
        public ResultMsg login(String id, Object a) {
            return null;
        }
    }

    微信登录

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:30
     * @description: 微信登录
     */
    public class LoginForWechatAdapter implements LoginAdapter {
        @Override
        public boolean support(Object adapter) {
            return adapter instanceof LoginForWechatAdapter;
        }
    
        @Override
        public ResultMsg login(String id, Object a) {
            return null;
        }
    }

    登录适配器

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 12:18
     * @description: 继承原有业务实现扩展接口  结合策略模式、工厂模式、适配器模式
     */
    public class PassportForThirdAdapter extends SinginService implements IPassportForThird {
        @Override
        public ResultMsg loginForQQ(String openId) {
    /*      //        适配器不一定要接口
            LoginAdapter adapter = new LoginForQQAdapter();
            if (adapter.support(adapter)){
                return adapter.login(openId,adapter);
            }
            return null;
    //      用工厂模式简化代码
            */
    //        策略模式体现,用户选择不同的逻辑
            return processLogin(openId, LoginForQQAdapter.class);
        }
    
        @Override
        public ResultMsg loginForWechat(String openId) {
         /*   //        适配器不一定要接口
            LoginAdapter adapter = new LoginForWechatAdapter();
            if (adapter.support(adapter)){
                return adapter.login(openId,adapter);
            }
            return null;*/
    
            //        策略模式体现,用户选择不同的逻辑
            return processLogin(openId, LoginForWechatAdapter.class);
        }
    
        @Override
        public ResultMsg loginForSina(String openId) {
            //        策略模式体现,用户选择不同的逻辑
            return processLogin(openId, LoginForSinaAdapter.class);
        }
    
        @Override
        public ResultMsg loginForToken(String token) {
    
            //        策略模式体现,用户选择不同的逻辑
            return processLogin(token, LoginForTokenAdapter.class);
        }
    
        @Override
        public ResultMsg loginForTelphone(String telphone, String code) {
            //        策略模式体现,用户选择不同的逻辑
            return processLogin(telphone, LoginForTelAdapter.class);
        }
    
        @Override
        public ResultMsg loginForRegister(String username, String passport) {
            super.regist(username, passport);
            return super.login(username, passport);
        }
    
        /**
         * 简单工厂模式使用,简化代码,不用每一个登录方式都要写一遍判断并实例化对应的对象
         */
        private ResultMsg processLogin(String key, Class<? extends LoginAdapter> clazz) {
            try {
                //        适配器不一定要接口
                LoginAdapter adapter = clazz.newInstance();
           //    判断传过来的适配器是否能处理指定的逻辑
    if (adapter.support(adapter)) { return adapter.login(key, adapter); } else { return null; } } catch (Exception e) { e.printStackTrace(); } return null; } }

    测试用例

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 14:48
     * @description:
     */
    public class PassportTest {
    
        public static void main(String[] args) {
            PassportForThirdAdapter passportForThirdAdapter = new PassportForThirdAdapter();
            passportForThirdAdapter.loginForQQ("fdsfa");
    
        }
    }

    附上类结构图(查看类图快捷键Ctrl+Alt+Shift+U)

     

    总结

    适配器模式的优点:

    • 能提高类的透明性和复用,现有的类复用但不需要改变。
    • 目标类和适配器类解耦,提高程序的可扩展性。
    • 在很多业务场景中符合开闭原则。

    适配器模式的缺点:

    • 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
    • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。可能需要了解系统的整个架构才能看懂。

    以上对适配器模式的介绍到此结束,欢迎批评指正。 附:源码地址

  • 相关阅读:
    协方差矩阵
    VS2010+C#+EmguCV 配置详解
    OpenCv,EmguCv及.net之间的互动(The Interaction of OpenCv, EmguCv AND .net)
    EmguCV学习 与opencv的区别和联系
    redis新手入门,摸不着头脑可以看看<二>
    java常用工具类[待补充]
    redis新手入门,摸不着头脑可以看看<一>
    用java代码发送http请求
    Date和long类型互转
    WEB-INF目录下文件复制的几种方式
  • 原文地址:https://www.cnblogs.com/itzhoucong/p/14262184.html
Copyright © 2020-2023  润新知