一. 使用场景
1.1 改造背景
平时工作应该都处理过用户登录的需求, 传统的写法是这样的, controller --> service --> mysql, 如果出现多种用户登录类型, 那么service层的代码就会变成这样, 这种写法很简单, 但是如果哪天增加了新的登录方式, 且不止一种, 那么就要在下面增加很多else..if , 太多的else..if 语句影响阅读代码, 特别是对旧代码的修改, 很容易出现bug, 这就是违背了面向对象最基本的设计原则 : 开闭原则, 也就是对修改关闭, 对拓展开放
@Override
public UserInfo doUserLogin(UserInfo userInfo) {
if (UserLoginTypeEnum.BOSS_LOGIN.equals(type)) {
// 老板登录
} else if (UserLoginTypeEnum.STORE_LOGIN.equals(type)) {
// 顾客登录
}
// 等多的登录类型
}
二. 改造方案
2.1 策略模式
那如何设计代码, 既可以增加新的登录功能, 又不会修改原有代码? 最近在研究设计模式, 使用 策略模式, 就可以很好地解决问题, 在《head first 设计模式》书中是这样描述策略模式的 : 策略模式定了了算法族, 分别封装起来, 让他们之间可以互相替换, 此模式让算法的变化独立于使用算法的客户
我们来看策略模式的类图
2.2 根据类图设计接口
该接口定义了总策略, 具体实现哪一种策略要根据它的实现类决定
package com.demo07.strategy;
import com.demo07.model.UserInfo;
import com.demo07.service.UserService;
/**
* 用户登录策略
* @author canxiusi.yan
* @description UserLogin
* @date 2022/2/13 15:56
*/
public interface UserLogin {
/**
* 用户登录, 返回该用户信息
* @return
*/
UserInfo doLogin(UserInfo userInfo, UserService userService);
}
2.3 设计实现类
老板登录策略
/**
* @author canxiusi.yan
* @description BossUserLogin
* @date 2022/2/13 16:04
*/
@Component("boss")
public class BossLoginStrategy implements LoginStrategy {
@Override
public BaseRspVo<UserModel> doLogin(UserLoginRequest userLoginRequest) {
// 模拟查询DB
UserModel bossUser = UserModel.builder().name("managerUser").id("2").build();
return BaseRspVo.successInstance(bossUser);
}
}
顾客登录策略
/**
* 具体策略
* @author canxiusi.yan
* @description StoreUserLogin
* @date 2022/2/13 15:57
*/
@Component("store")
public class StoreLoginStrategy implements LoginStrategy {
@Override
public BaseRspVo<UserModel> doLogin(UserLoginRequest userLoginRequest) {
UserModel storeUser = UserModel.builder().name("storeUser").id("1").build();
return BaseRspVo.successInstance(storeUser);
}
}
controller 调用
/**
* @author canxiusi.yan
* @description UserController
* @date 2022/2/13 16:05
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
ApplicationContext applicationContext;
@PostMapping("/doLogin")
public BaseRspVo<UserModel> doStoreLogin(@RequestBody UserLoginRequest userLoginRequest) {
LoginStrategy loginStrategy = applicationContext.getBean(userLoginRequest.getLoginTypeEnum().getType(), LoginStrategy.class);
return loginStrategy.doLogin(userLoginRequest);
}
}
2.4 总结
这样一来, 如果后续增加新的登录方式, 只需要增加新的策略实现类去实现 LoginStrategy 接口, 而不用修改原有的代码, 甚至不用关心旧代码是怎么实现的, 很好的遵循了设计原则