策略模式得益于按照开闭原则进行设计,各个具体算法按照单一职责原则设计; 提高了代码的复用性,对客户隐藏具体策略 (算法) 的实现细节,彼此完全独立,扩展其余不受影响;避免if-else 或 switch 分支语句判断;其缺点在于客户端必须知道所有的策略类,增加了系统中类的个数。
一、前言
策略模式可能是在工作中使用最多的,也是在面试中最常提到的,代码重构和优化的必备!
小编之前也是一直说,其实没有真正的实战;最近有了机会实战了一下,来分享一下使用心得和在企业级的使用!
二、策略模式
1、什么是策略模式
策略模式,英文全称是 Strategy Design Pattern。在 GoF 的《设计模式》一书中,它是这样定义的:
定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码)。
2、策略模式结构组成
- Strategy:抽象策略类,一般为接口或者策略类。
- ConcreteStrategy:具体算法实现策略类。
- Context:环境或者上下文类,用于统一执行具体策略。
其实以上三部分用白话文来解释就是:
需要一个接口和策略进行规范和约束接口和方法,这时需要一些具体的实现算法类去继承或者实现刚刚的接口和策略,最后通过一个环境或者上下文,也可以叫做工厂根据类型进行调用具体的算法!
3、使用场景
- 避免冗长的 if-else 或 switch 分支判断
- 需要动态地在几种算法中选择一种
- 对客户隐藏具体策略 (算法) 的实现细节,彼此完全独立,扩展其余不受影响
具体场景一般为:
- 支付方式选择
- 打折、满减方式选择
- 根据类型调用不同的系统
4、优缺点
优点:
- 扩展性好
- 符合开闭原则
- 符合单一职责原则
- 可读性好
- 便于维护
- 避免多层判断
缺点:
三、策略模式实战
1、实战例子
今天小编根据春夏秋冬四季需要做不同的事情来演示一下策略模式的使用方案:
需求是:
如果是春天,就要去放风筝
如果是夏天,就要去游泳
如果是秋天,就要去看枫叶
如果是冬天,就要去打雪仗
没有使用策略模式的话,肯定就是
if-if else进行实现!
下面就带大家体会一下具体使用哈!!
2、策略接口
/**
* 四季策略
* @author wangzhenjun
* @date 2022/12/1 9:30
*/
public interface SeasonsStrategy {
/**
* 根据季节去执行不同的方案
* @param seasons
* @return
*/
String execute(String seasons);
}
3、春季具体实现
/**
* 春季具体实现
* @author wangzhenjun
* @date 2022/12/1 9:34
*/
// 指定容器的名称,不指定默认为类名称首字母小写
@Component("spring")
public class SpringStrategy implements SeasonsStrategy{
@Override
public String execute(String seasons) {
return seasons + "来了!我们一起去放风筝吧!";
}
}
4、夏季具体实现
/**
* 夏季具体实现
* @author wangzhenjun
* @date 2022/12/1 9:34
*/
// 指定容器的名称,不指定默认为类名称首字母小写
@Component("summer")
public class SummerStrategy implements SeasonsStrategy{
@Override
public String execute(String seasons) {
return seasons + "来了!我们一起去游泳吧!";
}
}
5、秋季具体实现
/**
* 秋季具体实现
* @author wangzhenjun
* @date 2022/12/1 9:34
*/
// 指定容器的名称,不指定默认为类名称首字母小写
@Component("autumn")
public class AutumnStrategy implements SeasonsStrategy{
@Override
public String execute(String seasons) {
return seasons + "来了!我们一起去放看枫叶吧!";
}
}
6、冬季具体实现
/**
* 冬季具体实现
* @author wangzhenjun
* @date 2022/12/1 9:34
*/
// 指定容器的名称,不指定默认为类名称首字母小写
@Component("winter")
public class WinterStrategy implements SeasonsStrategy{
@Override
public String execute(String seasons) {
return seasons + "来了!我们一起去打雪仗吧!";
}
}
7、上下文工厂实现
private Map<String, SeasonsStrategy> seasonsMap;这是最重要的,很多时候我们都知道怎么进行策略和实现怎么写,不知道怎么统一去放进去,来进行调用,可以自己放在map中。当然spring已经给我们组装好了,只要按需调用即可!
核心:Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类!
/**
* 环境或者上下文类,用于统一执行具体策略
* @author wangzhenjun
* @date 2022/12/1 9:56
*/
@Component
public class SeasonsFactory {
/**
* Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类
*/
@Autowired
private Map<String, SeasonsStrategy> seasonsMap;
/**
* 处理四季统一入口方法
* @param seasons
* @param beanName
* @return
*/
public String handle(String seasons,String beanName){
// 根据bean的名称获取对应的算法处理类
SeasonsStrategy seasonsStrategy = seasonsMap.get(beanName);
String execute = seasonsStrategy.execute(seasons);
return execute;
}
}
8、controller处理
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private SeasonsFactory seasonsFactory;
@GetMapping("/strategyTest/{seasons}/{beanName}")
public Result strategyTest(@PathVariable("seasons") String seasons,@PathVariable("beanName") String beanName){
String handle = seasonsFactory.handle(seasons, beanName);
return Result.success(handle);
}
}
9、测试
http://localhost:8087/test/strategyTest/春天/spring。
http://localhost:8087/test/strategyTest/夏天/summer。
四、总结
在策略模式中定义了一系列算法,将每一个算法封装起来,并让它们可以相互替换,互不影响。
策略模式得益于按照开闭原则进行设计,各个具体算法按照单一职责原则设计; 提高了代码的复用性,对客户隐藏具体策略 (算法) 的实现细节,彼此完全独立,扩展其余不受影响;避免if-else 或 switch 分支语句判断;其缺点在于客户端必须知道所有的策略类,增加了系统中类的个数。
在日常开发一般用于消除多重判断,有时候不要为了用设计模式而用设计模式,一定要结合业务场景,过度设计也是很致命的!!