设计模式(五)之适配器模式(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)

 

总结

适配器模式的优点:

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

适配器模式的缺点:

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

 

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

posted @ 2021-01-12 16:31  IT学无止境99  阅读(50)  评论(0编辑  收藏  举报