1. 生活中的适配器

2. 适配器模式概念
适配器模式(Adapter Pattern)是指将一个类的接口转化成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作,属于结构型设计模式。
3. 适配器模式的适用场景
- 已经存在的类,它的方法与需求不匹配(方法结果相同或者相似)的情况
- 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护由于不同产品、不同厂家造成功能类似而接口不同情况下的解决方案。
- 案例展示:(直流AC与交流DC转换)
AC220直流220v类
public class AC220 {public int outputAcc220V(){int output = 220 ;System.out.println("输出电流"+ output + "V");return output;}}
直流电的输入类
DC5 交流5接口
public interface DC5 {int outputDC5V();}
交流电的输出接口
PowerAdapter电流转换类
public class PowerAdapter implements DC5{private AC220 ac220;public PowerAdapter(AC220 ac220){this.ac220 = ac220;}@Overridepublic int outputDC5V() {int adapterInput = ac220.outputAcc220V();int adapterOutput = adapterInput / 44;System.out.println("使用PowerAdapter输入AC:" + adapterInput + "V,输出DC:" + adapterOutput + "V");return adapterOutput;}}
通过将传入的直流电转化为可用的交流电输出
测试类
public class PowerAdapterTest {public static void main(String[] args) {DC5 dc5 = new PowerAdapter(new AC220());dc5.outputDC5V();}}
4. 登录场景的适配器
登录的流程无非就是输入用户名、密码和验证码然后验证成功登录,但是不同的业务处理登录的逻辑会有所不同,比如说现在流行的qq登录、支付宝登录、微信登录等等。
SiginService 登录service
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;}}
SigninForThirdService第三方登录适配
public class SigninForThirdService 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);}}
Member类
ublic 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;}}
ResultMsg类
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;}}
这两个类不需要解释太多,通过查看第三方登录逻辑可以发现,登录的核心逻辑依然是login方式,但是为了适配不同的应用场景,选择了调用了不同的适配方法达到了需求。
5. 登录场景再升级
IPassportForThird第三方登录接口只扩展
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);}
PassportForThirdAdapter第三方接口实现类
public class PassportForThirdAdapter extends SiginService implements IPassportForThird{@Overridepublic ResultMsg loginForQQ(String id) {return processLogin(id,LoginForQQAdapter.class);}@Overridepublic ResultMsg loginForWechat(String id) {return processLogin(id, LoginForWechatAdapter.class);}@Overridepublic ResultMsg loginForToken(String token) {return processLogin(token, LoginForTokenAdapter.class);}@Overridepublic ResultMsg loginForTelphone(String telphone, String code) {return processLogin(telphone, LoginForTelAdapter.class);}@Overridepublic ResultMsg loginForRegist(String username, String passport) {super.regist(username,passport);return super.login(username,passport);}/*** 简单工厂模式* @param key* @param clazz* @return*/private ResultMsg processLogin(String key,Class<? extends LoginAdapter> clazz){try {//适配器不一定要实现接口LoginAdapter adapter = clazz.newInstance();if (adapter.support(adapter)){return adapter.login(key,adapter);}}catch (Exception e){e.printStackTrace();}return null;}}
这个类作为第三方接口实现类,重写了所有接口。这里最后提供了一个使用简单工厂实现的方法,用于所有第三方验证的接口调用适配,对所传入的参数类型进行判断,属于哪一种类型就会自动调用该类型接口,达到了自动识别的效果。
LoginAdapter 登录适配器接口
/*** 在适配器中这个接口是可有可无的 不要跟模板模式混淆* 模板模式一定是抽象类 这里仅仅是一个接口* @author hkfire* @create 2021-07-12 14:40* @description*/public interface LoginAdapter {boolean support(Object adapter);ResultMsg login(String id,Object a);}
这个登录适配器接口用于后续每一种情况的登录验证,主要包含两个方法,一个是判断是否支持当前适配器类型,还有个方法就是登录方法。
LoginForQQAdapter第三方QQ登录适配器
public class LoginForQQAdapter implements LoginAdapter{@Overridepublic boolean support(Object adapter) {return adapter instanceof LoginForQQAdapter;}@Overridepublic ResultMsg login(String id, Object a) {return null;}}
LoginForSinaAdapter第三方新浪登录适配器
public class LoginForSinaAdapter implements LoginAdapter{@Overridepublic boolean support(Object adapter) {return adapter instanceof LoginForSinaAdapter;}@Overridepublic ResultMsg login(String id, Object a) {return null;}}
LoginForTelAdapter第三方手机登录适配器
public class LoginForTelAdapter implements LoginAdapter{@Overridepublic boolean support(Object adapter) {return adapter instanceof LoginForTelAdapter;}@Overridepublic ResultMsg login(String id, Object a) {return null;}}
LoginForTokenAdapter第三方token登录适配器
public class LoginForTokenAdapter implements LoginAdapter {@Overridepublic boolean support(Object adapter) {return adapter instanceof LoginForTokenAdapter;}@Overridepublic ResultMsg login(String id, Object a) {return null;}}
LoginForWechatAdapter第三方微信登录适配器
public class LoginForWechatAdapter implements LoginAdapter{@Overridepublic boolean support(Object adapter) {return adapter instanceof LoginForWechatAdapter;}@Overridepublic ResultMsg login(String id, Object a) {return null;}}
这样我们整个登录流程的适配器案例就算是写完了,那么回头梳理一下整个调用思路,首先想好你登录的场景,qq或者是微信,那么需要new一个PassportForThirdAdapter出来,用IPassportForThird接收,然后调用你的登录方式方法,传入匹配的类型,例如调用的这个processLogin(id,LoginForQQAdapter.class),之后会根据你传入的参数类型去判断选择哪一个适配器类型,这里选择的是qq登录,然后去验证是否支持qq登录,会去调用该适配器的support方法,返回true则适用,false则不适用,验证成功了,再去调用登录方法,进行参数值验证。
适配器模式在我们spring的aop中使用的比较常见,aop中有个AdvisorAdapter通知类,可以根据不同的配置达到不同通知的效果,感兴趣的小伙伴可以翻翻源码。
6. 适配器模式的缺点
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
