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

类图

在这里插入图片描述

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

  • 1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
  • 2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。有点亡羊补牢的感觉。
    生活中也非常的应用场景,例如电源插转换头、手机充电转换头、显示器转接头。

适配器模式入门小demo

在这里插入图片描述
适配的接口

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();
    }
}

在这里插入图片描述

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

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

简单的写法:

在这里插入图片描述

/**
 * 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源码的方式,反射生成适配类 来实现这个需求:

在这里插入图片描述
在这里插入图片描述

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.接口适配器
之前的一篇适配的文章有提到。

适配器模式的本质

在这里插入图片描述

什么时候使用适配器模式

在这里插入图片描述

相关模式

在这里插入图片描述
在这里插入图片描述


个人微信公众号:
搜索: 怒放de每一天
不定时推送相关文章,期待和大家一起成长!!
在这里插入图片描述


展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 游动-白 设计师: 上身试试
应支付0元
点击重新获取
扫码支付

支付成功即可阅读