zoukankan      html  css  js  c++  java
  • 设计模式(五)之适配器模式(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)

     

    总结

    适配器模式的优点:

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

    适配器模式的缺点:

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

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

  • 相关阅读:
    Vue路由机制
    谷歌浏览器打不开应用商店的解决方法
    Vue报错——Component template should contain exactly one root element. If you are using vif on multiple elements, use velseif to chain them instead.
    Vue.js学习之——安装
    Vue使用axios无法读取data的解决办法
    关于localstorage存储JSON对象的问题
    2013年整体计划
    个人喜欢的警语收集
    Linux防火墙的关闭和开启
    Flex修改title 转载
  • 原文地址:https://www.cnblogs.com/itzhoucong/p/14262184.html
Copyright © 2011-2022 走看看