当前位置:网站首页>为什么我不再推荐枚举策略模式?
为什么我不再推荐枚举策略模式?
2022-08-02 14:44:00 【Park33448】
一、为什么讲策略模式
策略模式,应该是工作中比较常用的设计模式,调用方自己选择用哪一种策略完成对数据的操作,也就是“一个类的行为或其算法可以在运行时更改”
我个人的理解是 将一些除了过程不同其他都一样的函数封装成策略,然后调用方自己去选择想让数据执行什么过程策略。常见的例子为根据用户分类推荐不同的排行榜(用户关注点不一样,推荐榜单就不一样)
和单例模式一样,随着时间发展,我不再推荐经典策略模式,更推荐简单策略用枚举策略模式,复杂地用工厂策略模式。下面引入一个例子,我们的需求是:对一份股票数据列表,给出低价榜、高价榜、涨幅榜。这其中只有排序条件的区别,比较适合作为策略模式的例子
二、经典策略模式
数据DTO
@Datapublic class Stock { // 股票交易代码 private String code; // 现价 private Double price; // 涨幅 private Double rise;}抽象得到的策略接口
public interface Strategy { /** * 将股票列表排序 * * @param source 源数据 * @return 排序后的榜单 */ List<Stock> sort(List<Stock> source);}实现我们的策略类
/** * 高价榜 */public class HighPriceRank implements Strategy { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getPrice).reversed()) .collect(Collectors.toList()); }}/** * 低价榜 */public class LowPriceRank implements Strategy { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getPrice)) .collect(Collectors.toList()); }}/** * 高涨幅榜 */public class HighRiseRank implements Strategy { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getRise).reversed()) .collect(Collectors.toList()); }}经典的Context类,
public class Context { private Strategy strategy; public void setStrategy(Strategy strategy) { this.strategy = strategy; } public List<Stock> getRank(List<Stock> source) { return strategy.sort(source); }}于是 我们顺礼成章地得到调用类--榜单实例RankServiceImpl
@Servicepublic class RankServiceImpl { /** * dataService.getSource() 提供原始的股票数据 */ @Resource private DataService dataService; /** * 前端传入榜单类型, 返回排序完的榜单 * * @param rankType 榜单类型 * @return 榜单数据 */ public List<Stock> getRank(String rankType) { // 创建上下文 Context context = new Context(); // 这里选择策略 switch (rankType) { case "HighPrice": context.setStrategy(new HighPriceRank()); break; case "LowPrice": context.setStrategy(new LowPriceRank()); break; case "HighRise": context.setStrategy(new HighRiseRank()); break; default: throw new IllegalArgumentException("rankType not found"); } // 然后执行策略 return context.getRank(dataService.getSource()); }}我们可以看到经典方法,创建了一个接口、三个策略类,还是比较啰嗦的。调用类的实现也待商榷,新增一个策略类还要修改榜单实例(可以用抽象工厂解决,但是复杂度又上升了)。加之我们有更好的选择,所以此处不再推荐经典策略模式
三、基于枚举的策略模式
这里对这种简单的策略,推荐用枚举进行优化。枚举的本质是创建了一些静态类的集合。
我下面直接给出例子,大家可以直观感受一下
枚举策略类
public enum RankEnum { // 以下三个为策略实例 HighPrice { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getPrice).reversed()) .collect(Collectors.toList()); } }, LowPrice { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getPrice)) .collect(Collectors.toList()); } }, HighRise { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getRise).reversed()) .collect(Collectors.toList()); } }; // 这里定义了策略接口 public abstract List<Stock> sort(List<Stock> source);}对应的调用类也得以优化,榜单实例RankServiceImpl
@Servicepublic class RankServiceImpl { /** * dataService.getSource() 提供原始的股票数据 */ @Resource private DataService dataService; /** * 前端传入榜单类型, 返回排序完的榜单 * * @param rankType 榜单类型 形似 RankEnum.HighPrice.name() * @return 榜单数据 */ public List<Stock> getRank(String rankType) { // 获取策略,这里如果未匹配会抛 IllegalArgumentException异常 RankEnum rank = RankEnum.valueOf(rankType); // 然后执行策略 return rank.sort(dataService.getSource()); }}可以看到,如果策略简单的话,基于枚举的策略模式优雅许多,调用方也做到了0修改,但正确地使用枚举策略模式需要额外考虑以下几点。
- 枚举的策略类是公用且静态,这意味着这个策略过程不能引入非静态的部分,扩展性受限
- 策略模式的目标之一,是优秀的扩展性和可维护性,最好能新增或修改某一策略类时,对其他类是无改动的。而枚举策略如果过多或者过程复杂,维护是比较困难的,可维护性受限
四、基于工厂的策略模式
为了解决良好的扩展性和可维护性,我更推荐以下利用spring自带beanFactory的优势,实现一个基于工厂的策略模式。
策略类改动只是添加了@Service注解,并指定了Service的value属性
/** * 高价榜 * 注意申明 Service.value = HighPrice,他是我们的key,下同 */@Service("HighPrice")public class HighPriceRank implements Strategy { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getPrice).reversed()) .collect(Collectors.toList()); }}/** * 低价榜 */@Service("LowPrice")public class LowPriceRank implements Strategy { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getPrice)) .collect(Collectors.toList()); }}/** * 高涨幅榜 */@Service("HighRise")public class HighRiseRank implements Strategy { @Override public List<Stock> sort(List<Stock> source) { return source.stream() .sorted(Comparator.comparing(Stock::getRise).reversed()) .collect(Collectors.toList()); }}调用类修改较大,接入借助spring工厂特性,完成策略类
@Servicepublic class RankServiceImpl { /** * dataService.getSource() 提供原始的股票数据 */ @Resource private DataService dataService; /** * 利用注解@Resource和@Autowired特性,直接获取所有策略类 * key = @Service的value */ @Resource private Map<String, Strategy> rankMap; /** * 前端传入榜单类型, 返回排序完的榜单 * * @param rankType 榜单类型 和Service注解的value属性一致 * @return 榜单数据 */ public List<Stock> getRank(String rankType) { // 判断策略是否存在 if (!rankMap.containsKey(rankType)) { throw new IllegalArgumentException("rankType not found"); } // 获得策略实例 Strategy rank = rankMap.get(rankType); // 执行策略 return rank.sort(dataService.getSource()); }}若读者使用的不是Spring,也可以找找对应框架的工厂模式实现,或者自己实现一个抽象工厂
工厂策略模式会比枚举策略模式啰嗦,但也更加灵活、易扩展性和易维护。故简单策略推荐枚举策略模式,复杂策略才推荐工厂策略模式
边栏推荐
- 什么是Nacos?
- 祝蔡徐坤生日快乐!
- Qt | 设置部件大小 sizeHint、minimumSizeHint、sizePolicy、stretch factor
- 2022/7/15,我的人生中第一篇博客,不忘初心,砥砺前行!
- 单例模式(singleton pattern)
- 树状DP(记忆化搜索)PAT甲级 1079 1090 1106
- 2022 VMware下载安装教程
- 该死的单元测试,写起来到底有多痛?
- 阅读,是最便宜的高贵
- 【 Leetcode string, the string transform/hexadecimal conversion 】 HJ1. The length of the string last word HJ2. Calculation of a certain number of characters appear HJ30. String merging processing
猜你喜欢

初入c语言

李开复花上千万投的缝纫机器人,团队出自大疆

Servlet运行原理_API详解_请求响应构造进阶之路(Servlet_2)
![解决(An error happened during template parsing (template: “class path resource [templates/...]](/img/d9/df0027ed6063a27f6be117b9d67a06.png)
解决(An error happened during template parsing (template: “class path resource [templates/...]

【无标题】

mysql 索引使用与优化

【Frequency Domain Analysis】Spectral leakage, frequency resolution, picket fence effect

阿里云上安装MQTT服务器

2022年低压电工考试试题及在线模拟考试

【Untitled】
随机推荐
SQL实现将多行记录合并成一行
基于ip的证书
HDU1561 树形背包dp+边界优化 0ms过题
【无标题】
【学习笔记之菜Dog学C】自定义类型详解(结构体+枚举+联合)
MySQL查询
什么是Knife4j?
Selenium元素定位方法总结
Servlet运行原理_API详解_请求响应构造进阶之路(Servlet_2)
Typora永久使用,彻底解决This beta version of Typora is expired.
兆骑科创双创服务平台,创业赛事活动,投融资对接平台
【go-zero】go-zero 框架踩坑指南 Q&A (持续更新中)
(数学基础)第三章-3.2-标准记号和常用函数
PostGresql listen与notify命令
OneFlow源码解析:Op、Kernel与解释器
【Leetcode字符串--字符串变换/进制的转换】HJ1.字符串最后一个单词的长度 HJ2.计算某字符出现次数 HJ30.字符串合并处理
初入c语言
Qt | 关于容器类的一些总结
ShardingSphere基本介绍及核心概念
leetcode 504. Base 7 七进制数 (简单)