适配器模式实战场景和本质

简介: 适配器模式实战场景和本质

类图



1dc618a0ed9580ce8bfa6facb208c08f.png

适配器模式的应用场景:


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


适配器适用于以下几种业务场景:


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


2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。有点亡羊补牢的感觉。


生活中也非常的应用场景,例如电源插转换头、手机充电转换头、显示器转接头。


适配器模式入门小demo


5d4c6812c8535adbb050f4ddf2e1bce8.png

适配的接口


public interface DC5 {
    int outputDC5V();
}


被适配的接口/类


public class AC220 {
    public int outputAC220V() {
        int output = 220;
        System.out.println("输出交流电: " + output + "v");
        return output;
    }
}


适配类:


public class PowerAdapter implements DC5 {
    private AC220 ac220;
    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }
    @Override
    public int outputDC5V() {
        int adapterInput = ac220.outputAC220V();
        int adapetOutput = adapterInput / 44;
        System.out.println("使用PowerAdapter 输入AC: " + adapetOutput + "V " + "输出DC:" + adapetOutput + "V");
        return adapetOutput;
    }
}


测试


public class OjectAdapterTest {
    public static void main(String[] args) {
        DC5 dc5 = new PowerAdapter(new AC220());
        dc5.outputDC5V();
    }
}

1dc618a0ed9580ce8bfa6facb208c08f.png


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


下面我们来一个实际的业务场景,利用适配模式来解决实际问题。年纪稍微大一点的小伙伴一定经历过这样一个过程。我们很早以前开发的老系统应该都有登录接口,但是随着业务的发展和社会的进步,单纯地依赖用户名密码登录显然不能满足用户需求了。现在,我们大部分系统都已经支持多种登录方式,如QQ 登录、微信登录、手机登录、微博登录等等,同时保留用户名密码的登录方式。虽然登录形式丰富了,但是登录后的处理逻辑可以不必改,同样是将登录状态保存到session,遵循开闭原则。


简单的写法:


5d4c6812c8535adbb050f4ddf2e1bce8.png

/**
 * Title: ResultMsg
 * Description: 统一返回格式
 *
 * @author hfl
 * @version V1.0
 * @date 2020-05-21
 */
public class ResultMsg {
    private int code;
    private String msg;
    private Object data;
    public ResultMsg(int code, String msg, Object 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;
    }
}


登录参数:


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;
    }
}
/**
 * 最简单的 注册登录方式(被适配类Adaptee)
 */
public class SiginService {
    /**
     * 注册方法
     *
     * @param username
     * @param password
     * @return
     */
    public ResultMsg regist(String username, String password) {
        return new ResultMsg(200, "注册成功", new Member());
    }
    /**
     * 登录的方法
     *
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password) {
        return null;
    }
}
/**
 * 新增第三方登录方式, 
 * 兼容原来的注册登录(通过继承方式实现)
 */
public class SinginForThirdService extends SiginService {
    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;
    }
    public ResultMsg loginForRegist(String username,String password){
        super.regist(username,null);
        return super.login(username,null);
    }
}


测试: 原来的用户名和密码登录 和现在新增的 通过qq和微信的方式都可以使用到。


public class SiginForThirdServiceTest {
    public static void main(String[] args) {
        SinginForThirdService service = new SinginForThirdService();
        service.login("tom","123456");
        service.loginForQQ("sdfasdfasf");
        service.loginForWechat("sdfasfsa");
    }
}


优雅的写法


上面的写法可以满足适配,但是,spring源码中采用更优雅的方式,我们也可以模仿下spring源码的方式,反射生成适配类 来实现这个需求:

1dc618a0ed9580ce8bfa6facb208c08f.png

5d4c6812c8535adbb050f4ddf2e1bce8.png

public class PassportForThirdAdapter extends SiginService implements IPassportForThird {
    @Override
    public ResultMsg loginForQQ(String id) {
        return processLogin(id, LoginForQQAdapter.class);
    }
    @Override
    public ResultMsg loginForWechat(String id) {
        return processLogin(id, LoginForWechatAdapter.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 loginForRegist(String username, String passport) {
        super.regist(username, null);
        return super.login(username, null);
    }
    //这里用到了简单工厂模式及策略模式
    private ResultMsg processLogin(String key, Class<? extends LoginAdapter> clazz) {
        try {
            LoginAdapter adapter = clazz.newInstance();
            if (adapter.support(adapter)) {
                System.out.println("传入参数是: key"+"; 调用 "+adapter+" 的login方法");
                return adapter.login(key, adapter);
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}



* Title: IPassportForThird
 * Description: 第三方登录兼容接口
 *
 * @author hfl
 * @version V1.0
 * @date 2020-05-23
 */
public interface IPassportForThird {
    /**
     * QQ 登录
     * @param id
     * @return
     */
    ResultMsg loginForQQ(String id);
    /**
     * 微信登录
     * @param id
     * @return
     */
    ResultMsg loginForWechat(String id);
    /**
     * 记住登录状态后自动登录
     * @param token
     * @return
     */
    ResultMsg loginForToken(String token);
    /**
     * 手机号登录
     * @param telphone
     * @param code
     * @return
     */
    ResultMsg loginForTelphone(String telphone, String code);
    /**
     * 注册后自动登录
     * @param username
     * @param passport
     * @return
     */
    ResultMsg loginForRegist(String username, String passport);
}


public interface LoginAdapter {
    boolean support(Object adapter);
    ResultMsg login(String id, Object adapter);
}


public class LoginForQQAdapter implements LoginAdapter{
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForQQAdapter;
    }
    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}


/**
 * 新浪微博登录
 */
public class LoginForSinaAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForSinaAdapter;
    }
    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}


public class LoginForTelAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return  adapter instanceof LoginForTelAdapter;
    }
    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}


/**
 * Title: LoginForTokenAdapter
 * Description: token自动登录
 *
 * @author hfl
 * @version V1.0
 * @date 2020-05-23
 */
public class LoginForTokenAdapter implements LoginAdapter{
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForTokenAdapter;
    }
    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}
/**
 * Title: LoginForWechatAdapter
 * Description: 微信登录
 *
 * @author hfl
 * @version V1.0
 * @date 2020-05-23
 */
public class LoginForWechatAdapter  implements LoginAdapter{
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof LoginForWechatAdapter;
    }
    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}


public class PassportTest {
    public static void main(String[] args) {
        IPassportForThird passportForThird = new PassportForThirdAdapter();
        //原来的接口 可用
        passportForThird.loginForRegist("admin","admin");
        //新增适配的接口 同样可用
        passportForThird.loginForQQ("11");
    }
}


至此,我们在遵循开闭原则的前提下,完整地实现了一个兼容多平台登录的业务场景。

当然,我目前的这个设计也并不完美,仅供参考,感兴趣的小伙伴可以继续完善这段代

码。例如适配器中的参数目前是写死为String,改为Object[]应该更合理。


适配器的3种方式


1.类适配器


2.对象适配器器


3.接口适配器


之前的一篇适配的文章有提到。


适配器模式的本质


1dc618a0ed9580ce8bfa6facb208c08f.png


什么时候使用适配器模式


5d4c6812c8535adbb050f4ddf2e1bce8.png


相关模式


46a9d80a6e05e4e3b19d57a0ee70bcdf.png66ba272a0bfc97be54a5fa679e3d5482.png


相关文章
|
2天前
|
设计模式 Java 中间件
23种设计模式,适配器模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】适配器模式(Adapter Pattern)是一种结构型设计模式,它的主要目标是让原本由于接口不匹配而不能一起工作的类可以一起工作。适配器模式主要有两种形式:类适配器和对象适配器。类适配器模式通过继承来实现适配,而对象适配器模式则通过组合来实现
38 4
|
2天前
|
安全 Java 数据安全/隐私保护
|
2天前
|
设计模式
二十三种设计模式全面解析-装饰器模式-超越继承的灵活装扮
二十三种设计模式全面解析-装饰器模式-超越继承的灵活装扮
|
7月前
|
设计模式 Java 关系型数据库
【设计模式——学习笔记】23种设计模式——适配器模式Adapter(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——适配器模式Adapter(原理讲解+应用场景介绍+案例介绍+Java代码实现)
51 0
|
1天前
|
设计模式 存储 SQL
第四篇 行为型设计模式 - 灵活定义对象间交互
第四篇 行为型设计模式 - 灵活定义对象间交互
|
1天前
|
设计模式 存储 缓存
第三篇 结构型设计模式 - 简化复杂系统的结构
第三篇 结构型设计模式 - 简化复杂系统的结构
|
2天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~结构型]处理多维度变化——桥接模式
[设计模式Java实现附plantuml源码~结构型]处理多维度变化——桥接模式
|
2天前
|
设计模式 Java
根据真实业务场景去实现一下设计模式中的装饰者模式
根据真实业务场景去实现一下设计模式中的装饰者模式
18 0
|
9月前
|
存储 算法 Java
Java集合重点知识详解——优点以及内部继承关系
Java集合重点知识详解——优点以及内部继承关系
71 0
|
11月前
|
设计模式 存储 安全
【Java设计模式 面向对象设计思想】一 再谈面向对象和封装、抽象、继承、多态四大特性
【Java设计模式 面向对象设计思想】一 再谈面向对象和封装、抽象、继承、多态四大特性
56 0