架构师内功心法,中国与日本交流电压兼容性问题的适配器模式详解
一、适配器模式的应用场景
我们在现实生活见识到的电源插头转换器、手机充电转换头、显示器转接头等都是适配器模式的体现。适配器模式(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 类,它也有多个子类,类图如下:
三、适配器模式的优缺点
优点:
-
能提高类的透明性和复用性,现有的类复用但不需要改变;
-
目标类和适配器类解耦,提高程序的扩展性;
-
在很多业务场景中符合开闭原则。
缺点:
-
适配器编写过程需要全面考虑,可能会增加系统的复杂性;
-
增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
来源:迅闻网
原文地址:https://www.cnblogs.com/1994july/p/12994196.html
- Jdbc知识点全整理,你值得拥有 (2)
- 抓住“新代码”的影子 —— 基于GoAhead系列网络摄像头多个漏洞分析
- Python 用OPEN读文件报错 ,路径以及r
- python 如何设置多线程
- R语言读CSV、txt文件方式以及read.table read.csv 和readr(大数据读取包)
- python 多进程设置 整理版本1
- 11g Dataguard中的snapshot standby特性(r8笔记第49天)
- 物化视图刷新结合ADG的尝试 (r8笔记第47天)
- 关于CPU使用率高的awr分析(r8笔记第46天)
- 图形工具和命令行的博弈-swingbench配置(r8笔记第63天)
- 手把手教你用LDA特征选择
- 一个关于执行计划的小问题测试(r8笔记第60天)
- 【Go 语言社区】www.golangweb.com通过工信部审核,正式挂牌社区域名
- golang 算法课程 正式开课--第一季 第1节
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Java开源框架中的设计模式以及应用场景
- 统一定制API返回格式,我只做了这几件事
- 《一起学sentinel》三、Slot的子类及实现之NodeSelectorSlot和ClusterBuilderSlot
- 《一起学sentinel》四、Slot的子类及实现之LogSlot和StatisticSlot
- 《一起学sentinel》五、Slot的子类及实现之AuthoritySlot和SystemSlot
- Python 之pyaudio使用随笔
- Determining 32 vs 64 bit in C++
- 在线、离线激活鉴权实战
- Spark vs Dask Python生态下的计算引擎
- 表达差异基因分析
- threadlocal记录
- 2020-09-22:已知两个数的最大公约数,如何...
- 【超详细】分布式一致性协议 - Paxos
- MySQL 8.0新特性 — 降序索引
- TRTC横竖屏切换2,重力感应