当前位置:网站首页>策略模式替代 if else
策略模式替代 if else
2022-07-29 12:12:00 【妃宫千早】
1. 什么是策略模式?
策略模式其实也是在解耦,把策略的定义、创建、使用这三个部分解耦开来,因为本身策略模式也是基于接口编程,这样其实可以简单的理解客户端调用使用接口进行编程,可以通过工厂方法创建对应的策略模式,进而完成对应的程序功能。这么说起来有点拗口,我们直接看程序吧。
public interface Strategy {
void alg();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void alg() {
//具体的算法...
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void alg() {
//具体的算法...
}
}
策略类的定义比较简单,仅仅包含一个策略接口和一组实现这个接口的策略类。这样客户端就可以根据不同的策略灵活的编程了,通常我们是使用工厂方法根据条件创建不同的策略模式进行使用。
public class StrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>();
static {
strategies.put("A", new ConcreteStrategyA());
strategies.put("B", new ConcreteStrategyB());
}
public static Strategy getStrategy(String type) {
return strategies.get(type);
}
}
这样就完成了初版的策略模式,其实我们只是使用的 map 进行了策略的路由,如果考虑到开放封闭原则,我们的程序还是存在很大的问题,这里留下一个悬念,下文中的真实例子会解开这个问题。
public class Main {
public static void main(String[] args) {
//...
StrategyFactory factory = new StrategyFactory();
factory.getStrategy("A").alg();
//...
}
}
说到这里其实大家已经看明白了策略模式就是通过接口和一组实现类的方式去掉了大量使用 if-else 的逻辑,那么我们接下来针对实际的例子做一下吧,这样更有体感。
2. 实际例子
编开发的码问(mawen.co)社区目前只支持了 Github 登录,因为 Github 在国外的原因登录起来比较容易超时,于是小编打算增加 Gitee 的登录,这样无论登录还是学习成本都会降低,原来只有 Github 登录的代码逻辑如下,主要逻辑就是通过 Github 的接口获取 Github 用户信息:
AccessTokenDTO accessTokenDTO = new AccessTokenDTO();
String accessToken = githubProvider.getAccessToken(accessTokenDTO);
GithubUser githubUser = githubProvider.getUser(accessToken);
if (githubUser != null && githubUser.getId() != null) {
//……登录成功逻辑
return "redirect:/";
} else {
//……登录失败逻辑
return "redirect:/";
}
如果简单粗暴的使用 if-else 完成 Gitee 的逻辑接入,那么代码会是如下的样子。
AccessTokenDTO accessTokenDTO = new AccessTokenDTO();
User user;
if(type == "github") {
String accessToken = githubProvider.getAccessToken(accessTokenDTO);
user = githubProvider.getUser(accessToken);
} else if(type == "gitee") {
String accessToken = giteeProvider.getAccessToken(accessTokenDTO);
user = giteeProvider.getUser(accessToken);
}
if (user != null && user.getId() != null) {
//……登录成功逻辑
return "redirect:/";
} else {
//……登录失败逻辑
return "redirect:/";
}
那么如果增加 10 个平台呢?代码会越来越多,而且完全违背的开放封闭原则,那么是时候展示策略模式真正的实力了。
首先我们创建一个策略接口和两个策略实现,这里有一个约定,我们平时开发使用了什么设计模式就要以这个设计模式名称结尾,所以具体的改造代码如下。
public interface UserStrategy {
// 通过 code 和 state 获取对应平台的用户信息
User getUser(String code,String state);
}
public class GithubUserStrategy implements UserStrategy {
// 通过 code 和 state 获取对应平台的用户信息
public User getUser(String code,String state) {
// ……根据 Github 信息获取用户资料
}
}
public class GiteeUserStrategy implements UserStrategy {
// 通过 code 和 state 获取对应平台的用户信息
public User getUser(String code,String state) {
// ……根据 Gitee 信息获取用户资料
}
}
同时我们也创建了一个工厂类,把创建具体的策略的实例的逻辑内聚起来。
public class UserStrategyFactory {
private static final Map<String, UserStrategy> strategies = new HashMap<>();
static {
strategies.put("github", new GithubUserStrategy());
strategies.put("gitee", new GiteeUserStrategy());
}
public static UserStrategy getStrategy(String type) {
return strategies.get(type);
}
}
我们重新回到增加 Gitee 的代码逻辑,修改成如下样子,是不是清爽了很多?
String type = "";
UserStrategy userStrategy = userStrategyFactory.getStrategy(type);
User user = userStrategy.getUser(code,state);
if (user != null && user.getId() != null) {
//……登录成功逻辑
return "redirect:/";
} else {
//……登录失败逻辑
return "redirect:/";
}
这时候我们细细品味还是有瑕疵的,因为 UserStrategyFactory 中并没有解决开放封闭原则的根本问题,每次增加新的 UserStrategy 实现类还是需要修改代码的,那么我们继续往前走一步,把具体的判断是否匹配策略的逻辑内聚到策略里面来解决这个问题。
我们给 UserStrategy 增加一个方法,叫做 getSupportedType,用于返回当前策略支持的 type 类型,我们直接用代码说话。
public interface UserStrategy {
// 通过 code 和 state 获取对应平台的用户信息
User getUser(String code,String state);
String getSupportedType();
}
public class GithubUserStrategy implements UserStrategy {
// 通过 code 和 state 获取对应平台的用户信息
public User getUser(String code,String state) {
// ……根据 Github 信息获取用户资料
}
public String getSupportedType(){
return "github";
}
}
public class GiteeUserStrategy implements UserStrategy {
// 通过 code 和 state 获取对应平台的用户信息
public User getUser(String code,String state) {
// ……根据 Gitee 信息获取用户资料
}
public String getSupportedType(){
return "gitee";
}
}
然后简单修改一下工厂类
public class UserStrategyFactory {
List<UserStrategy> strategies = new ArrayList(){
{
this.add(new GithubUserStrategy());
this.add(new GiteeUserStrategy());
}};
public static UserStrategy getStrategy(String type) {
for (UserStrategy userStrategy : strategies) {
if(type == userStrategy.getSupportedType()) {
return userStrategy;
}
}
}
}
这样以后,如果增加了新的策略实现,我们只需要增加到 strategies 里面就可以解决问题了,但是如何动态的增加进去不需要修改代码呢?我们可以使用自定义注解,把所有标记自定义注解的类都加载进来或者使用 Spring 的 @Autowired 也会自动的把所有的子类注入到对应的父类集合里面,那么这样是不是就大功告成了呢?
另一种方式 使用枚举 作为key 对象作为value 存入Map中,
使用时 map.get参数为枚举 也可以返回
边栏推荐
- How much is the test environment, starting from the actual needs
- PHP uedtior报错 errorHandler is not defined
- 【每日SQL打卡】DAY 26丨餐馆营业额变化增长【难度中等】
- 考完PMP后有什么益处
- 【每日SQL打卡】DAY 22丨平均售价【难度中等】
- Collections.singletonList(T o)
- 【每日SQL打卡】DAY 26丨广告效果【难度简单】
- 文件上传漏洞
- 【Untitled】
- DAY 24 daily SQL clock 】 【 丨 weather types in different countries [difficult simple]
猜你喜欢
随机推荐
惠及6亿人 投资98亿 沿江高铁武宜段最新进展来了!
LMO·3rd - 报名通知
MFC学习备忘
piglit_get_gl_enum_from_name 参数遍历
puzzle(017.5)联动归位
使用Tenserboard可视化深度学习训练过程
2.1 Bubble sort (mercifully Sorting)
什么是DOM
PL/SQL 面向对象
The company has a new product, do you want to hire an agent?
【第三次自考】——总结
Recursion - Eight Queens Problem
金仓数据库 KingbaseES 客户端编程接口指南 - ODBC 驱动使用
SQL clock in daily DAY 21 丨 】 each post comments difficulty moderate 】 【
【表达式计算】表达式计算问题的通用解法(练习加强版,含总结)
【每日SQL打卡】DAY 27丨每次访问的交易次数【难度困难-提前放出来】
DAY 25 丨 daily SQL clock 】 【 o team number [difficult medium]
吴恩达老师机器学习课程笔记 06 逻辑回归
Collections.singletonList(T o)
ECCV 2022 | 基于关系查询的时序动作检测方法









