架构师内功心法,中国与日本交流电压兼容性问题的适配器模式详解

Posted 1994july

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了架构师内功心法,中国与日本交流电压兼容性问题的适配器模式详解相关的知识,希望对你有一定的参考价值。

一、适配器模式的应用场景

我们在现实生活见识到的电源插头转换器、手机充电转换头、显示器转接头等都是适配器模式的体现。适配器模式(Adapter Pattern)是指一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作。

技术图片 技术图片 技术图片

适配器模式适用于以下几种应用场景:

  • 已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
  • 适配器模式不是在软件初始阶段考虑的设计模式,它是随着软件的发展,由于不同的产品、不同的厂家造成功能类似而接口不同的问题的解决方案,有点亡羊补牢的感觉。

1.1 中国与日本交流电压兼容性问题

大家都知道,我国民用电电压是220V,而日本的电压为100V,如果去过日本旅游的朋友应该会知道,我们在入住酒店时使用电源来给手机充电的时候就需要使用电源插头转换器来进行转换电压,这样才能给自己的手机进行充电。

首先创建AC220类 ,表示220V交流电中国标准:

public class AC220 {

    public BigDecimal outputAC220V(){
        BigDecimal output = new BigDecimal(220);
        System.out.println("中国输出交流电" + output + "V");
        return output;
    }
}

创建 DC100 接口,表示 100V 直流电的日本标准:

public interface DC100 {

    BigDecimal outputDC100V();

}

创建电源适配器 PowerAdapter 类:

public class PowerAdapter implements DC100 {

    private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public BigDecimal outputDC100V() {
        BigDecimal adapterInput = ac220.outputAC220V();
        BigDecimal adapterOutput = adapterInput.divide(new BigDecimal(2.2), 2, BigDecimal.ROUND_HALF_UP)  ;
        System.out.println("使用 PowerAdapter 输入 AC: " + adapterInput.intValue() +
                "V" + ",日本输出 DC: " + adapterOutput.intValue() + "V");
        return  adapterOutput;
    }

}

测试main方法:

public static void main(String[] args) {
    DC100 dc100 = new PowerAdapter(new AC220());
    dc100.outputDC100V();
 }

上面的案例中,通过增加 PowerAdapter电源适配器,实现了中国与日本交流电压二者的兼容性问题。

1.2 第三方登录自由适配的问题

现在浏览一些网站、博客、论坛以及手机APP的时候,我们可以以QQ、微信、新浪微博等多种方式进行系统登录。虽然登录形式丰富了,但是登录后的逻辑不用修改,同样是保存登录状态到session,遵循开闭原则。

首先创建统一的结果返回类:

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

    @Override
    public String toString() {
        return "ResultMsg{" +
                "code=" + code +
                ", msg=‘" + msg + ‘‘‘ +
                ", data=" + data +
                ‘}‘;
    }
}

创建用户类:

public class User {

    private String id;

    private String username;

    private String password;

    private String info;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    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 getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

之前老的登录逻辑类是这样子的:

public class SignService {

    /**
     * 注册
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password) {
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        return new ResultMsg(0, "注册成功", user);
    }

    /**
     * 登录
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password) {
        return null;
    }

}

我们会创建一个新的类继承原来的登录逻辑类,之前的代码我们无需做任何改动:

public class Sign4ThirdpartyService extends SignService {

    public ResultMsg login4Register(String username,String password){
        super.register(username,password);
        return super.login(username,password);
    }

    public ResultMsg login4Phone(String phone,String code){
        return null;
    }

    public ResultMsg login4Token(String token){
        //通过 token 拿到用户信息,然后再重新登陆了一次
        return null;
    }

    public ResultMsg login4QQ(String openId){
        //1、openId 是全局唯一
        //2、密码默认为 QQ_EMPTY
        //3、注册(在原有系统里面创建一个用户)
        //4、调用原来的登录方法
        return login4Register(openId,null);
    }

    public ResultMsg login4Wechat(String openId){
        return null;
    }

}

main方法:

 public static void main(String[] args) {
    Sign4ThirdpartyService service = new Sign4ThirdpartyService();
    service.login4QQ("qwqwqweyrtyrt13234");
}

当然我们还可以根据不同的登录方式创建不同的Adapter适配器,接着改造代码:

首先,创建 LoginAdapter 接口:

public interface LoginAdapter {

    boolean support(Object adapter);

    ResultMsg login(String id, Object adapter);

}

分别实现不同的登录适配,QQ 登录 LoginForQQAdapter:

public class Login4QQAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof Login4QQAdapter;
    }

    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}

微信登录 LoginForWechatAdapter:

public class Login4WechatAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof Login4WechatAdapter;
    }

    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}

token登录 Login4TokenAdapter:

public class Login4TokenAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof Login4TokenAdapter;
    }

    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}

手机号码登录 Login4TelphoneAdapter:

public class Login4TelphoneAdapter implements LoginAdapter {
    @Override
    public boolean support(Object adapter) {
        return adapter instanceof Login4TelphoneAdapter;
    }

    @Override
    public ResultMsg login(String id, Object adapter) {
        return null;
    }
}

创建与第三方兼容接口 IPassport4Thirdparty:

public interface IPassport4Thirdparty {

    /**
     * QQ 登录
     * @param id
     * @return
    23
     */
    ResultMsg login4QQ(String id);
    /**
     * 微信登录
     * @param id
     * @return
     */
    ResultMsg login4Wechat(String id);
    /**
     * 记住登录状态后自动登录
     * @param token
     * @return
     */
    ResultMsg login4Token(String token);
    /**
     * 手机号登录
     * @param telphone
     * @param code
     * @return
     */
    ResultMsg login4Telphone(String telphone, String code);
    /**
     * 注册后自动登录
     * @param username
     * @param passport
     * @return
     */
    ResultMsg login4Register(String username, String passport);

}

实现兼容适配器 Passport4ThirdpartyAdapter:

public class Passport4ThirdpartyAdapter extends SignService implements IPassport4Thirdparty {

    private ResultMsg processLogin(String key, Class<? extends LoginAdapter> clazz) {
        try {
            LoginAdapter loginAdapter = clazz.newInstance();
            if(loginAdapter.support(loginAdapter)) {
                return loginAdapter.login(key, loginAdapter);
            }else {
                return null;
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public ResultMsg login4QQ(String id) {
        return processLogin(id, Login4QQAdapter.class);
    }

    @Override
    public ResultMsg login4Wechat(String id) {
        return processLogin(id, Login4WechatAdapter.class);
    }

    @Override
    public ResultMsg login4Token(String token) {
        return processLogin(token, Login4TokenAdapter.class);
    }

    @Override
    public ResultMsg login4Telphone(String telphone, String code) {
        return processLogin(telphone, Login4TelphoneAdapter.class);
    }

    @Override
    public ResultMsg login4Register(String username, String passport) {
        super.register(username,null);
        return super.login(username,null);
    }
}

最后来看一下类图: 技术图片

至此,我们在遵循开闭原则的前提下,完整地实现了一个兼容多平台登录的业务场景。当然,目前的这个设计也并不完美,仅供参考。上面的代码可以说是策略模式、简单工厂模式和适配器模式的综合运用。

二、适配器模式在源码中的体现

2.1 Spring中的AdvisorAdapter接口

Spring 中适配器模式也应用得非常广泛,例如:SpringAOP 中的 AdvisorAdapter 类,它有三个实现类 MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter 和ThrowsAdviceAdapter,先来看顶层接口 AdvisorAdapter 的源代码:

public interface AdvisorAdapter {
    boolean supportsAdvice(Advice var1);

    MethodInterceptor getInterceptor(Advisor var1);
}

再看 MethodBeforeAdviceAdapter 类:

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    MethodBeforeAdviceAdapter() {
    }

    public boolean supportsAdvice(Advice advice) {
        return advice instanceof MethodBeforeAdvice;
    }

    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}

然后来看 AfterReturningAdviceAdapter 类:

class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
    AfterReturningAdviceAdapter() {
    }

    public boolean supportsAdvice(Advice advice) {
        return advice instanceof AfterReturningAdvice;
    }

    public MethodInterceptor getInterceptor(Advisor advisor) {
        AfterReturningAdvice advice = (AfterReturningAdvice)advisor.getAdvice();
        return new AfterReturningAdviceInterceptor(advice);
    }
}

最后来看 ThrowsAdviceAdapter 类:

class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
    ThrowsAdviceAdapter() {
    }

    public boolean supportsAdvice(Advice advice) {
        return advice instanceof ThrowsAdvice;
    }

    public MethodInterceptor getInterceptor(Advisor advisor) {
        return new ThrowsAdviceInterceptor(advisor.getAdvice());
    }
}

Spring 会根据不同的 AOP 配置来确定使用对应的 Advice,跟策略模式不同的一个方法可以同时拥有多个 Advice。

2.2 Spring中的HandlerAdapter

下面再来看一个 SpringMVC 中的 HandlerAdapter 类,它也有多个子类,类图如下:

技术图片

三、适配器模式的优缺点

优点:

  • 能提高类的透明性和复用性,现有的类复用但不需要改变;

  • 目标类和适配器类解耦,提高程序的扩展性;

  • 在很多业务场景中符合开闭原则。

缺点:

  • 适配器编写过程需要全面考虑,可能会增加系统的复杂性;

  • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

来源:迅闻网

以上是关于架构师内功心法,中国与日本交流电压兼容性问题的适配器模式详解的主要内容,如果未能解决你的问题,请参考以下文章

二架构师内功心法之设计模式

架构师内功心法,连接两个空间维度的桥接模式详解

架构师内功心法,连接两个空间维度的桥接模式详解

架构师内功心法之设计原则

架构师内功心法,注重方法调用顺序的建造者模式详解

架构师内功心法,注重方法调用顺序的建造者模式详解