为什么要使用策略模式?
想象这样一个用户登录的场景,用户可以通过以下多种渠道登录:
- 短信验证码登录
- 账号密码登录
- 第三方账号登录…
那么请考虑一下代码级别如何实现?
最土味的实现方式,相当丑陋。
public void login(String type) {
if ("sms".equals(type)) {
// todo 执行短信验证码登录逻辑
} else if ("pass".equals(type)) {
// todo 执行账号密码登录逻辑
} else {
// ......
}
}
此时,我们可以通过使用策略模式来更优雅地实现用户登录。
基于spring的@Component注解实现策略模式
关于策略模式的实现方式网上有很多,我不多赘述。我这次为大家带来一个比较小众,但是很优雅的实现方式,即基于@Component注解实现。话不多说,直接上代码。
- 首先你需要先定义一个模板(抽象类或者接口都行),然后在类上使用@Component注解。
@Component
public abstract class LoginStrategy {
public abstract String login();
}
or
@Component
public interface LoginInterface {
public String login();
}
- 接下来,新建你的登录策略类实现or继承模板类。
// 账号密码登录,@Component后的pass为别名的意思,也可以不写,不写默认是passLogin
@Component("pass")
@Slf4j
public class PassLogin implements LoginInterface{
@Override
public String login() {
log.info("执行账号密码登录逻辑");
return "PassLogin";
}
}
// 短信验证码登录
@Component("sms")
@Slf4j
public class SmsLogin implements LoginInterface{
@Override
public String login() {
log.info("执行短信验证码登录逻辑");
return "SmsLogin";
}
}
- 最关键的一步来了,新建LoginType类,来装载你上一步的登陆策略。
@Component
public class LoginType {
@Autowired
private Map<String, LoginInterface> strategy;
public LoginInterface strategy(String type) {
return strategy.get(type);
}
}
这里最核心的是,strategy这个map,key值为你登录策略类的@Component后的别名,不写默认是首字母小写的类名,value类型是模板类。
- 最后我们来验证一下:
@RestController
@RequestMapping("/strategy")
@Slf4j
public class StrategyController {
@Autowired
private LoginType loginType;
@GetMapping("/{type}")
public String type(@PathVariable("type") String type) {
log.info("type值为:{}", type);
LoginInterface strategy = loginType.strategy(type);
return strategy.login();
}
}
到这里,策略模式就算是实现了,是不是很神奇。
简单debug
断点进入,点击左边带对号的标记,能够看到LoginType下depends on了两个我们的登陆策略类。
strategy的size = 2,即spring已经自动帮我们将策略类装载进了strategy这个map里。
具体spring的底层实现本人目前还不清楚,后边有空深究了再补充吧。感兴趣的小伙伴欢迎评论区留言讨论。