适配器模式支持第三方登录
设配器模式的定义
原本一个类的接口实现的功能,由于需要进行扩展,支持另外一种形式的接口。那么 Adapter 模式使原本由于接口不兼容而不能在一起的类可以一起工作
场景
- 使用一个已经存在的类,但她的接口不符合你的需求
- 创建一个可以复用的类,该类可以与其他不相关的类或者不可预见的类(即那些接口,可能不一定兼容的类)协同工作
- (仅适用于对象 Adapter)使用一些已经存在的子类,但是不可能对每一个都进行 子类化以匹配它们的接口。对象适配器可以适配它的父类接口
参与者
- Target 定义 Client 需要使用与特定领域相关的接口
- Client 与符合 Target 接口对象一致
- Adaptee 定义一个已经存在的类,这个接口需要适配
- Adapter 对 Adaptee 的接口与 Target 接口进行适配
登录功能改造
场景: 我们原本只支持用户名+密码登录, 那么现在我们需要其支持第三方登录,但是最后面核心功能还是通过用户名 + 密码登录,但是第三方登录的入参完全不一样, 那么我们就可以通过定义一个设配器来完成登录功能的改造,而不影响其原本的登录功能
目标对象 Target 定义
目标对象 Target 是 client 要使用的接口对象
java
/**
* 目标角色
*/
public interface Login3rdTarget {
String loginByGitee(String code,String state);
String loginByWechat(String ...params);
String loginByQQ(String ...params);
String loginDefault(String username,String password);
}
源对象 Adaptee
源对象需要被适配的不兼容对象,也就是 UserService
java
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public String login(String account,String password) {
UserInfo userInfo = userRepository.findByUsernameAndUserPassword(account, password);
if (userInfo == null) {
return "account/ password error";
}
return "Login Success";
}
public String register(UserInfo userInfo) {
if (checkUserExist(userInfo.getUsername())) {
throw new RuntimeException("user already registed");
}
userInfo.setCreateTime(new Date());
userRepository.save(userInfo);
return "register success";
}
public boolean checkUserExist(String username) {
UserInfo userInfo = userRepository.findByUsername(username);
if (userInfo == null){
return false;
}
return true;
}
}
设配器对象 Adapter
充当中间转换角色,该对象将源对象转换成目标接口,也就是我们的 Login3rdAdapter
java
@Component
public class Login3rdAdapter extends UserService implements Login3rdTarget {
public Login3rdAdapter(UserRepository userRepository) {
super(userRepository);
}
@Override
public String loginByGitee(String code, String state) {
// 根据code 请求gitee 获取 access_token
// 根据access_token 获取用户信息
JSONObject userInfo = new JSONObject(); // 假设这个就是gitee返回回来的数据
String username = "gitee@" + userInfo.get("name");
String password = username;
return autoRegister3rdAndLogin(username,password);
}
private String autoRegister3rdAndLogin(String username, String password) {
if (super.checkUserExist(username)) {
// 如果第三方账号已注册, 直接登录
return super.login(username,password);
}
UserInfo userInfo = new UserInfo();
userInfo.setUsername(username);
userInfo.setUserPassword(password);
userInfo.setCreateTime(new Date());
super.register(userInfo);
return super.login(username,password);
}
@Override
public String loginByWechat(String... params) {
return null;
}
@Override
public String loginByQQ(String... params) {
return null;
}
@Override
public String loginDefault(String username, String password) {
return super.login(username,password);
}
}
Client 调用方
UserController 也就是我们的调用方
java
@RestController
public class UserController {
private final Login3rdAdapter login3rdAdapter;
public UserController(Login3rdAdapter login3rdAdapter) {
this.login3rdAdapter = login3rdAdapter;
}
@PostMapping("login")
public String login(@RequestParam String account, @RequestParam String password) {
return login3rdAdapter.login(account, password);
}
@PostMapping("register")
public String register(@RequestBody UserInfo userInfo) {
return login3rdAdapter.register(userInfo);
}
@PostMapping("login/gitee")
public String loginByGitee(@RequestParam String code,@RequestParam String state) {
return login3rdAdapter.loginByGitee(code, state);
}
}
从上述我们可以看到,我们通过 Adapter 完成了用户登录接口的改造
uml 类图
设用场景
- 重用现有的代码:适配器模式可以允许我们重用已有的类或接口,而不需要修改其原有的代码
- 集成老系统:当现有的系统不满足用户需求时,需要增加系统功能或接口。但是,老系统的接口可能与现有的技术、平台不兼容,此时可以采用适配器模式,将现有的接口适配为新的接口,从而实现新系统的集成。
- 集成第三方组件:在使用第三方组件时,可能由于它们实现的 API 不同而导致应用程序复杂,此时可以使用适配器模式,将第三方组件提供的 API 适配为自己需要的 API,方便在应用程序中进行调用
- 实现跨平台兼容:在不同平台、不同技术栈之间进行开发时,常常需要适配不同的接口,以使得不同的平台或技术栈之间能够相互兼容,此时可以使用适配器模式来处理各种不兼容问题。