0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么我不再推荐枚举策略模式?

jf_ro2CN3Fa 来源:芋道源码 2023-04-14 10:52 次阅读


一、为什么讲策略模式

策略模式,应该是工作中比较常用的设计模式,调用方自己选择用哪一种策略完成对数据的操作,也就是“一个类的行为或其算法可以在运行时更改”

我个人的理解是 将一些除了过程不同其他都一样的函数封装成策略,然后调用方自己去选择想让数据执行什么过程策略。常见的例子为根据用户分类推荐不同的排行榜(用户关注点不一样,推荐榜单就不一样)

和单例模式一样,随着时间发展,我不再推荐经典策略模式,更推荐简单策略用枚举策略模式,复杂地用工厂策略模式。下面引入一个例子,我们的需求是:对一份股票数据列表,给出低价榜、高价榜、涨幅榜。这其中只有排序条件的区别,比较适合作为策略模式的例子

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

二、经典策略模式

数据DTO

@Data
publicclassStock{

//股票交易代码
privateStringcode;

//现价
privateDoubleprice;

//涨幅
privateDoublerise;
}

抽象得到的策略接口

publicinterfaceStrategy{

/**
*将股票列表排序
*
*@paramsource源数据
*@return排序后的榜单
*/
Listsort(Listsource);
}

实现我们的策略类

/**
*高价榜
*/
publicclassHighPriceRankimplementsStrategy{

@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getPrice).reversed())
.collect(Collectors.toList());
}
}

/**
*低价榜
*/
publicclassLowPriceRankimplementsStrategy{

@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getPrice))
.collect(Collectors.toList());
}
}

/**
*高涨幅榜
*/
publicclassHighRiseRankimplementsStrategy{

@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getRise).reversed())
.collect(Collectors.toList());
}
}

经典的Context类,

publicclassContext{
privateStrategystrategy;

publicvoidsetStrategy(Strategystrategy){
this.strategy=strategy;
}

publicListgetRank(Listsource){
returnstrategy.sort(source);
}
}

于是 我们顺礼成章地得到调用类--榜单实例RankServiceImpl

@Service
publicclassRankServiceImpl{

/**
*dataService.getSource()提供原始的股票数据
*/
@Resource
privateDataServicedataService;

/**
*前端传入榜单类型,返回排序完的榜单
*
*@paramrankType榜单类型
*@return榜单数据
*/
publicListgetRank(StringrankType){
//创建上下文
Contextcontext=newContext();
//这里选择策略
switch(rankType){
case"HighPrice":
context.setStrategy(newHighPriceRank());
break;
case"LowPrice":
context.setStrategy(newLowPriceRank());
break;
case"HighRise":
context.setStrategy(newHighRiseRank());
break;
default:
thrownewIllegalArgumentException("rankTypenotfound");
}
//然后执行策略
returncontext.getRank(dataService.getSource());
}
}

我们可以看到经典方法,创建了一个接口、三个策略类,还是比较啰嗦的。调用类的实现也待商榷,新增一个策略类还要修改榜单实例(可以用抽象工厂解决,但是复杂度又上升了)。加之我们有更好的选择,所以此处不再推荐经典策略模式

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

三、基于枚举的策略模式

这里对这种简单的策略,推荐用枚举进行优化。枚举的本质是创建了一些静态类的集合。

我下面直接给出例子,大家可以直观感受一下

枚举策略类

publicenumRankEnum{
//以下三个为策略实例
HighPrice{
@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getPrice).reversed())
.collect(Collectors.toList());
}
},
LowPrice{
@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getPrice))
.collect(Collectors.toList());
}
},
HighRise{
@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getRise).reversed())
.collect(Collectors.toList());
}
};

//这里定义了策略接口
publicabstractListsort(Listsource);
}

对应的调用类也得以优化,榜单实例RankServiceImpl

@Service
publicclassRankServiceImpl{

/**
*dataService.getSource()提供原始的股票数据
*/
@Resource
privateDataServicedataService;

/**
*前端传入榜单类型,返回排序完的榜单
*
*@paramrankType榜单类型形似RankEnum.HighPrice.name()
*@return榜单数据
*/
publicListgetRank(StringrankType){
//获取策略,这里如果未匹配会抛IllegalArgumentException异常
RankEnumrank=RankEnum.valueOf(rankType);
//然后执行策略
returnrank.sort(dataService.getSource());
}
}

可以看到,如果策略简单的话,基于枚举的策略模式优雅许多,调用方也做到了0修改,但正确地使用枚举策略模式需要额外考虑以下几点。

  • 枚举的策略类是公用且静态,这意味着这个策略过程不能引入非静态的部分,扩展性受限
  • 策略模式的目标之一,是优秀的扩展性和可维护性,最好能新增或修改某一策略类时,对其他类是无改动的。而枚举策略如果过多或者过程复杂,维护是比较困难的,可维护性受限

四、基于工厂的策略模式

为了解决良好的扩展性和可维护性,我更推荐以下利用spring自带beanFactory的优势,实现一个基于工厂的策略模式。

策略类改动只是添加了@Service注解,并指定了Service的value属性

/**
*高价榜
*注意申明Service.value=HighPrice,他是我们的key,下同
*/
@Service("HighPrice")
publicclassHighPriceRankimplementsStrategy{

@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getPrice).reversed())
.collect(Collectors.toList());
}
}

/**
*低价榜
*/
@Service("LowPrice")
publicclassLowPriceRankimplementsStrategy{

@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getPrice))
.collect(Collectors.toList());
}
}

/**
*高涨幅榜
*/
@Service("HighRise")
publicclassHighRiseRankimplementsStrategy{

@Override
publicListsort(Listsource){
returnsource.stream()
.sorted(Comparator.comparing(Stock::getRise).reversed())
.collect(Collectors.toList());
}
}

调用类修改较大,接入借助spring工厂特性,完成策略类

@Service
publicclassRankServiceImpl{

/**
*dataService.getSource()提供原始的股票数据
*/
@Resource
privateDataServicedataService;
/**
*利用注解@Resource@Autowired特性,直接获取所有策略类
*key=@Service的value
*/
@Resource
privateMaprankMap;

/**
*前端传入榜单类型,返回排序完的榜单
*
*@paramrankType榜单类型和Service注解的value属性一致
*@return榜单数据
*/
publicListgetRank(StringrankType){
//判断策略是否存在
if(!rankMap.containsKey(rankType)){
thrownewIllegalArgumentException("rankTypenotfound");
}
//获得策略实例
Strategyrank=rankMap.get(rankType);
//执行策略
returnrank.sort(dataService.getSource());
}
}

若读者使用的不是Spring,也可以找找对应框架的工厂模式实现,或者自己实现一个抽象工厂。

工厂策略模式会比枚举策略模式啰嗦,但也更加灵活、易扩展性和易维护。故简单策略推荐枚举策略模式,复杂策略才推荐工厂策略模式。



审核编辑 :李倩



声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 框架
    +关注

    关注

    0

    文章

    297

    浏览量

    17046
  • 函数
    +关注

    关注

    3

    文章

    3904

    浏览量

    61310

原文标题:为什么我不再推荐枚举策略模式?

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    CYUS3014 RAM烧写成功,为什么枚举不起来?

    CYUSB3014 RAM显示烧写成功后,缺不再枚举,技术支持让更换芯片后,问题依旧,请问是否还有别的方式可以查找问题?
    发表于 02-29 07:13

    cyusb3014枚举的工作原理是什么?如果下载固件后不枚举,可能会是什么原因,原理是什么?

    用3014设计自己的板子,参考开发板kit3的原理图,插入计算机后可以被识别为Bootloader,采用USB启动模式,下载实例中的固件后,显示烧写成功,但设备在计算机中消失了,不再重新被
    发表于 02-29 07:11

    如何同时枚举SlaveFIFO和UART(CDC)?

    使用Re: Slave FIFO + UART Driver Setup中的程序,将img下载进FX3中,成功枚举出了“USB串行设备(COM13)”,但是并没有出现
    发表于 02-28 07:23

    枚举有多大?c语言枚举end的作用是什么?

    枚举有多大?c语言枚举end的作用是什么? 枚举在C语言中是一种常见的数据类型,用于定义一组相互关联的常量或者变量。它通常用于表示一系列可能的取值,使得程序更加易读和易维护。在C语言中,枚举
    的头像 发表于 01-19 14:19 214次阅读

    java switch case值能为枚举值吗

    Java中的switch语句可以接受枚举类型的值作为参数。在Java中,枚举是一种特殊的数据类型,它定义了一个固定数量的命名常量。因此,可以将枚举值作为switch语句的case。 首先,让我们来看
    的头像 发表于 11-30 14:41 1595次阅读

    C语言必备知识枚举

    枚举类型是ANSI C新标准所增加的。如果一个变量只有几种可能的值,可以定义为枚举类型。所谓“枚举是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
    的头像 发表于 11-29 16:07 486次阅读

    如何在Go中给GORM模型添加枚举类型

    GORM是一个流行的Go ORM,它允许我们定义结构体来表示数据库表,并提供一个接口来执行CRUD操作。枚举在编程中是一个有用的特性,它允许我们定义一个变量可以取的固定值集。在这篇文章中,我们将探索如何向GORM模型添加枚举
    的头像 发表于 11-28 15:36 745次阅读

    如何通过策略模式简化if-else

    相信大家日常开发中会经常写各种分支判断语句,比如 if-else ,当分支较多时,代码看着会比较臃肿,那么如何优化呢? 1、什么是策略模式? Define a family
    的头像 发表于 10-08 16:08 271次阅读
    如何通过<b class='flag-5'>策略</b><b class='flag-5'>模式</b>简化if-else

    什么是策略模式

    什么是策略模式 官话: 策略模式(Strategy Pattern): 定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略
    的头像 发表于 10-08 14:15 1804次阅读
    什么是<b class='flag-5'>策略</b><b class='flag-5'>模式</b>

    Java枚举的特点及用法

    在 Java 枚举出现之前,通常会使用常量类来表示一组固定的常量值,直到Java 1.5之后推出了枚举,那么枚举类型有哪些特点,它比常量类又好在哪里呢。 本文将分析一下枚举的特点及用法
    的头像 发表于 09-30 10:02 427次阅读

    C语言中枚举的基本概念和常见用法

    在C语言中,枚举是一种方便组织和表示一组相关常量的工具。枚举类型有助于提高代码的可读性和可维护性。本文将介绍C语言枚举的基本概念、语法和用法,以及一些高级技巧。
    发表于 08-17 15:32 776次阅读

    基于DWC2的USB驱动开发-高速设备枚举为全速设备问题案例分析

    列的文章。 二.问题   我们设计的UVC摄像头工作在高速模式,接一个第三方的主机用于显示和无线上传,主机也支持高速模式。     我们的UVC摄像头接PC测试都能正常枚举为高速,从来没有失败过。接第三方的主机时总是被
    的头像 发表于 07-10 17:12 770次阅读
    基于DWC2的USB驱动开发-高速设备<b class='flag-5'>枚举</b>为全速设备问题案例分析

    设计模式行为型:策略模式

    策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式
    的头像 发表于 06-07 11:18 441次阅读
    设计<b class='flag-5'>模式</b>行为型:<b class='flag-5'>策略</b><b class='flag-5'>模式</b>

    枚举类型的简单扩展学习

    定义枚举类型的值只能是整型常量,正数、负数和零都可以,但不可以是浮点数
    的头像 发表于 05-25 15:45 455次阅读

    usbusb8997_combo_v4.bin这个模块不再支持usb模式了吗?

    正在寻找文件 usbusb8997_combo_v4.bin。这个模块不再支持usb模式了吗? 谢谢
    发表于 05-06 08:44