当前位置:网站首页>策略模式替代 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参数为枚举 也可以返回
边栏推荐
- DAY 22 丨 page daily clock in SQL 】 【 recommend 【 difficulty moderate 】
- Based article 】 【 learn with Rust | Rust, variables and data types
- 【多线程】——Callable创建多线程
- QCon大会广州站它来了!独家定制双肩背包等你领取!
- MarkDown高阶语法手册
- Recursion - Eight Queens Problem
- The interviewer training courseware (very practical in-house training courseware)
- 【每日SQL打卡】DAY 25丨求团队人数【难度中等】
- ECCV 2022 | 基于关系查询的时序动作检测方法
- 【每日SQL打卡】DAY 27丨每次访问的交易次数【难度困难-提前放出来】
猜你喜欢

一次node文件操作过多排查过程总结

MarkDown高阶语法手册

XSS漏洞分析

2.2 Selection sort

JVM内存模型如何分配的?
Based article 】 【 learn with Rust | Rust, variables and data types

mapbox 地图 生成矢量数据圆

金仓数据库KingbaseES客户端编程接口指南-ODBC(6. KingbaseES ODBC 的扩展属性)

【Untitled】

Paddle frame experience evaluation and exchange meeting, the use experience of the product is up to you!
随机推荐
"Qidong well day lily" is the national geographical indications protection products?Ants investigation on July 29, the answer
DAY 26 daily SQL clock 】 【 丨 advertisement effect difficult simple 】 【
公司出了一款新产品,要不要招代理商?
2.2 Selection sort
我和 TiDB 的故事 | TiDB 对我不离不弃,我亦如此
力扣sql刷题(四)
什么是DOM
Distributed Configuration Center of Infrastructure
piglit_get_gl_enum_from_name 参数遍历
【一起学Rust | 基础篇】Rust基础——变量和数据类型
mapbox 地图 生成矢量数据圆
DAY 25 daily SQL clock 】 【 丨 different sex daily score a total difficulty moderate 】 【
TiDB 操作实践 -- 备份与恢复
DAY 25 丨 daily SQL clock 】 【 o team number [difficult medium]
【我的OpenGL学习进阶之旅】向量点乘和叉乘的几何意义
【年中总结】创业3年,越来越穷,还是坚持架构平台
飞桨框架体验评测交流会,产品的使用体验由你来决定!
惠及6亿人 投资98亿 沿江高铁武宜段最新进展来了!
金仓数据库 KingbaseES 客户端编程接口指南 - ODBC 驱动使用
LeetCode_容斥原理_中等_223.矩形面积