适配器模式(Adapter):结构型模式之一,主要作用是兼容和转换,当项目扩展中需要兼容原来的老的代码时候,就可以考虑适配器模式了。
适配器模式在Spring源码中也有大量使用,其中AOP的实现里面对 BeforeAdvice、AfterAdvice、ThrowsAdvice 三种通知类型的支持实际上是借 助适配器模式来实现的,这样的好处是使得框架允许用户向框架中加入自己想要支持的任何一种通知类 型,上述三种通知类型是 Spring AOP 模块定义的,它们是 AOP 联盟定义的 Advice 的子类型。
举一个实际工作中的场景,在比较老的项目中,原来的登录方法主要是用的用户名(username),密码(passwords)方式登录,但随着项目发展,现在需要扩展加入微信(Wechat),微博(Sina),QQ(qq),手机号(telephone)登录方式,但是之前的登录比较稳定,用户量也比较大,不是太喜欢随便扩展,这个时候开始考虑使用适配器模式。
下面是类图:
其中有用户类User,登录注册返回信息类ResultMsg,老的登录服务类SignInService,新扩展的第三方登录类SignInForThirdService.
原来的User类实现:
package Adapter;
public class User {
private String username;
private String password;
private String mid;
private String info;
public User() {
}
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:
package Adapter;
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;
}
}
老的登录类SignInService:
package Adapter;
public class SignInService {
/**
* 注册方法
* @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){
System.out.println("登陆成功");
return null;
}
}
新扩展的SignInForThirdService:
package Adapter;
public class SignInForThirdService extends SignInService{
public ResultMsg loginForQQ(String openId){
//1、openId是全局唯一,我们可以把它当做是一个用户名(加长)
//2、密码默认为QQ_EMPTY
//3、注册(在原有系统里面创建一个用户)
//4、调用原来的登录方法
String QQDefaultPasswords = "QQ_EMPTY";
//这里省略查重验证,默认为新用户,实际项目执行会有
System.out.println("QQ登录");
return loginForRegister(openId,QQDefaultPasswords);
}
public ResultMsg loginForWechat(String openId){
String WechatDefaultPasswords = "WECHAT_EMPTY";
System.out.println("wechat登录");
return loginForRegister(openId,WechatDefaultPasswords);
}
public ResultMsg loginForToken(String token){
//通过token拿到用户信息,然后再重新登陆了一次
User user = new User();
System.out.println("token自动登录");
return login(user.getUsername(),user.getPassword());
}
public ResultMsg loginForTelephone(String telephone,String code){
String telephoneDefaultPasswords = "TELEPHONE_EMPTY";
System.out.println("手机号登录");
return loginForRegister(telephone,telephoneDefaultPasswords);
}
public ResultMsg loginForRegister(String username,String password){
super.register(username,password);
return super.login(username,password);
}
public ResultMsg login(String username,String password){
super.register(username,password);
return super.login(username,password);
}
}
以上是简略的一个模块,这个例子中用的是继承的方式,实际也可以通过依赖的方式,实际上是比较复杂的,以QQ登录为例,选择地三方QQ登录,会传过来一个单独的openid,此时回去查找数据库看有没有这个用户,如果是新的用户,那么就会重新生成一个默认QQ用户密码的用户,然后调用老的SignInSevice的注册和登录方法。
测试类:
package Adapter;
public class SignInForThirdServiceTest {
public static void main(String[] args) {
SignInForThirdService service = new SignInForThirdService();
//不改变原来的代码,也要能够兼容新的需求
//还可以再加一层策略模式
service.loginForQQ("sdfgdgfwresdf9123sdf");
service.loginForTelephone("1560017471","sdha");
service.loginForToken("dsajdsakldjksafjhfkasljkla");
service.loginForWechat("dhafkahkjdsada");
}
}
执行结果:
以上就是一个简单的适配器模式的例子,在实际中可以看到,当项目需要扩展和兼容的时候,可以考虑使用适配器模式,在spring源码中,大部分适配器模式都是以adapter结尾的类。