对于一些老系统应该都有登录接口,但是随着业务的发展和社会的进步,单纯地依赖用户名密码登录显然不能满足用户需求了。现在,我们大部分系统都已经支持多种登录方式,如QQ登录,微信登录,手机登录,微博登录等等,同时保留用户名密码的登录方式。虽然登录形式丰富了,但是登录后的处理逻辑可以不必改,同样是将登录状态保存到session,遵循开闭原则。
首先创建统一的返回结果
ResultMsg类
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class ResultMsg {
private int code;
private String msg;
private Object data;
public ResultMsg(int code, String msg,Object data) {
this.code = code;
this.data = data;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
假设老系统的登录逻辑SiginService
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class SiginService {
/**
* 注册方法
*/
public ResultMsg regist(String username,String password){
return new ResultMsg(200,"注册成功",new Member());
}
/**
* 登录方法
*/
public ResultMsg login(String username,String password){
return null;
}
}
相关的Member实体类
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class Member {
private String username;
private String password;
private String mid;
private String info;
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
创建一个新的类继承原来的逻辑,运行非常稳定的代码我们不去改动
package loginadapter;
/**稳定的方法不去动,直接继承下来
* Created by Lenovo on 2019-7-18.
*/
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 loginForRegist(String username,String password){
super.regist(username, null);
return super.login(username,null);
}
}
测试类:
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class SigninForThirdServiceTest {
public static void main(String[] args){
SigninForThirdService service=new SigninForThirdService();
//不改变原来的代码,也要能够兼容新的需求
//还可以再加一层策略模式
service.loginForQQ("ssddfsdgsdsgs");
}
}
通过一个简单的适配,完成了代码兼容。当然,我们代码还可以更加优雅,根据不同的登录方式,创建不同的Adapter。首先,创建LoginAdapter接口:
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public interface LoginAdapter {
boolean support(Object adapter);
ResultMsg login(String id,Object adapter);
}
下面分别实现不同的登录适配:
QQ
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class LoginForQQAdapter implements LoginAdapter {
@Override
public ResultMsg login(String id, Object adapter) {
return null;
}
@Override
public boolean support(Object adapter) {
return adapter instanceof LoginForQQAdapter;
}
}
微博
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class LoginForSinaAdapter implements LoginAdapter {
@Override
public ResultMsg login(String id, Object adapter) {
return null;
}
@Override
public boolean support(Object adapter) {
return adapter instanceof LoginForSinaAdapter;
}
}
手机号
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class LoginForTelAdapter implements LoginAdapter {
@Override
public ResultMsg login(String id, Object adapter) {
return null;
}
@Override
public boolean support(Object adapter) {
return adapter instanceof LoginForSinaAdapter;
}
}
Token
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
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;
}
}
微信
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
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;
}
}
创建第三方登录兼容接口
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
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 loginForRegister(String username,String passport);
}
实现兼容
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
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);
}
public ResultMsg loginForRegister(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)) {
return adapter.login(key, adapter);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
最后的测试类
package loginadapter;
/**
* Created by Lenovo on 2019-7-18.
*/
public class PassportTest {
public static void main(String[] args){
IPassportForThird passportForThird=new PassportForThirdAdapter();
passportForThird.loginForQQ("");
}
}
注:适配器模式主要解决的是功能兼容的问题,单场景的适配和策略不会有对比,但是多场景的适配可能会产生联想和混淆。其实此处我在每个适配器上都加了一个support,用来判断是否兼容,support方法的参数也是Object的,而support来自于接口。适配器的实现逻辑并不依赖于接口,完全可以将LoginAdapter接口去掉。上面的代码也可以说是策略模式,简单工厂模式和适配器模式的综合应用,LoginAdapter接口这里体现策略模式的思想,实现兼容 的PassportForThirdAdapter类中的processLogin方法体现了适配器模式的思想,里面的实现体现了简单工厂的思想。