使用类适配器重构第三方登录自由适配
在《适配器模式详解》一节我们已经了解了适配器模式,下面使用类适配器来实现一个实际的业务场景,解决实际问题。
年纪稍微大一点的小伙伴一定知道,很早以前开发的老系统大部分都有登录接口。随着业务的发展和社会的进步,单纯的依赖用户名和密码登录显然已经不能满足用户的需求了。
现在,大部分系统都支持多种登录方式,如 QQ 登录、微信登录、手机登录、微博登录、自动登录等。虽然登录形式丰富,但是登录后的处理逻辑不用变,都是将登录状态保存到 Session,遵循开闭原则。
首先创建统一的返回结果 ResultMsg 类:
到这里,在遵循开闭原则的前提下,我们完整的实现了一个兼容多平台登录的业务场景。
适配器模式主要解决的是功能兼容的问题,单场景的适配和策略不会有对比,但是多场景的适配可能会让大家很容易产生联想和混淆。
此处我们在每个适配器上都加了一个 support() 方法,用来判断是否兼容。support() 方法的参数也是 Object 的,而 support() 来自于接口。适配器的实现逻辑并不依赖于接口,完全可以将 ILoginAdapter 接口去掉。加上接口,只是为了代码规范。
上面的代码也可以说是策略模式,简单工厂模式和适配器模式的综合应用,LoginAdapter 接口这里体现策略模式的思想,实现兼容的 SignForThirdAdapter 类中的 processLogin 方法体现了适配器模式的思想,里面的实现体现了简单工厂的思想。
年纪稍微大一点的小伙伴一定知道,很早以前开发的老系统大部分都有登录接口。随着业务的发展和社会的进步,单纯的依赖用户名和密码登录显然已经不能满足用户的需求了。
现在,大部分系统都支持多种登录方式,如 QQ 登录、微信登录、手机登录、微博登录、自动登录等。虽然登录形式丰富,但是登录后的处理逻辑不用变,都是将登录状态保存到 Session,遵循开闭原则。
首先创建统一的返回结果 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; } // 省略set和get方法 }假设在老系统中,处理登录逻辑的代码在 SignService 类中。
public class SignService { /** * 注册方法 * * @param username * @param password * @return */ public ResultMsg register(String username, String password) { return new ResultMsg(200, "注册成功", new user()); } /** * 登录方法 * * @param username * @param password * @return */ public ResultMsg login(String username, String password) { return null; } }为了遵循开闭原则,不修改老系统的代码。下面开启代码重构之路,创建 User 类。
public class User { private String username; private String password; private String mid; private String info; // 省略set和get方法 }我们也不改动运行非常稳定的代码,创建目标接口角色 ISignForThird。
public interface ISignForThird { ResultMsg loginForQQ(String openId); ResultMsg loginForWechat(String openId); ResultMsg loginForToken(String token); ResultMsg loginForTelphone(String phone, String code); }增加 Adapter 角色实现兼容。创建一个新的类 SignForThirdAdapter,继承原来的逻辑,运行非常稳定的代码我们不去改动。
public class SignForThirdAdapter extends SignService implements ISignForThird { public ResultMsg loginForQQ(String openId) { return loginForRegist(openId, null); } public ResultMsg loginForWechat(String openId) { return loginForRegist(openId, null); } public ResultMsg loginForToken(String token) { return loginForRegist(token, null); } public ResultMsg loginForTelphone(String phone, String code) { return loginForRegist(phone, null); } private ResultMsg loginForRegist(String username, String password) { if (null == password) { password = "THIRD_EMPTY"; } super.register(username, password); return super.login(username, password); } }客户端测试代码如下:
public static void main(String[] args) { SignForThirdAdapter adapter = new SignForThirdAdapter(); adapter.login("新宝库", "123456"); adapter.loginForQQ("1154852654"); adapter.loginForWechat("455256225"); }这样通过这么一个简单的适配动作,我们完成了代码兼容。当然,代码还可以更加优雅。
使用接口适配器优化代码
下面根据不同的登录方式,创建不同的 Adapter。首先创建 LoginAdapter 接口。public interface ILoginAdapter { boolean support(Object object); ResultMsg login(String id, Object adapter); }然后创建一个抽象类 AbstractAdapter 继承 SignService 原有的功能,同时实现 ILoginAdapter 接口,再分别实现不同的登录适配。
public abstract class AbstractAdapter extends SignService implements ILoginAdapter { protected ResultMsg loginForRegist(String username, String password) { if (null == password) { password = "THIRD_EMPTY"; } super.register(username, password); return super.login(username, password); } }QQ 登录 LoginForQQAdapter 如下。
public class LoginForQQAdapter extends AbstractAdapter { public boolean support(Object adapter) { return adapter instanceof LoginForQQAdapter; } public ResultMsg login(String id, Object adapter) { if (!support(adapter)) { return null; } //accesseToken //time return super.loginForRegist(id, null); } }手机登录 LoginForTelAdapter 如下。
public class LoginForTelAdapter extends AbstractAdapter { public boolean support(Object adapter) { return adapter instanceof LoginForTelAdapter; } public ResultMsg login(String id, Object adapter) { return super.loginForRegist(id, null); } }Token 自动登录 LoginForTokenAdapter 如下。
public class LoginForTokenAdapter extends AbstractAdapter { public boolean support(Object adapter) { return adapter instanceof LoginForTokenAdapter; } public ResultMsg login(String id, Object adapter) { return super.loginForRegist(id, null); } }微信登录 LoginForWechatAdapter 如下。
public class LoginForWechatAdapter extends AbstractAdapter { public boolean support(Object adapter) { return adapter instanceof LoginForWechatAdapter; } public ResultMsg login(String id, Object adapter) { return super.loginForRegist(id, null); } }接着创建适配器 SignForThirdAdapter 类,实现目标接口 ISignForThird 完成兼容。
public class SignForThirdAdapter extends SignService implements ISignForThird { public ResultMsg loginForQQ(String openId) { return loginForRegist(openId, null); } public ResultMsg loginForWechat(String openId) { return loginForRegist(openId, null); } public ResultMsg loginForToken(String token) { return loginForRegist(token, null); } public ResultMsg loginForTelphone(String phone, String code) { return loginForRegist(phone, null); } private ResultMsg loginForRegist(String username, String password) { if (null == password) { password = "THIRD_EMPTY"; } super.register(username, password); return super.login(username, password); } }客户端测试代码如下。
public static void main(String[] args) { ISignForThird adapter = new SignForThirdAdapter(); adapter.loginForQQ("ssfsfafxzsds"); }最后来看如下图所示的类图:

到这里,在遵循开闭原则的前提下,我们完整的实现了一个兼容多平台登录的业务场景。
学到这里,大家可能觉得适配器模式和策略模式区别不大。上述设计并不完美,仅供参考,感兴趣的小伙伴可以继续完善这段代码。比如,适配器类中的参数类型目前设置为 String,改为 Object[] 应该更合理。
适配器模式主要解决的是功能兼容的问题,单场景的适配和策略不会有对比,但是多场景的适配可能会让大家很容易产生联想和混淆。
此处我们在每个适配器上都加了一个 support() 方法,用来判断是否兼容。support() 方法的参数也是 Object 的,而 support() 来自于接口。适配器的实现逻辑并不依赖于接口,完全可以将 ILoginAdapter 接口去掉。加上接口,只是为了代码规范。
上面的代码也可以说是策略模式,简单工厂模式和适配器模式的综合应用,LoginAdapter 接口这里体现策略模式的思想,实现兼容的 SignForThirdAdapter 类中的 processLogin 方法体现了适配器模式的思想,里面的实现体现了简单工厂的思想。
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算