策略模式使用

article/2025/8/22 19:18:44

策略模式

策略模式就是定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换;它可以替换继承关系,避免使用多重条件转移语句

典型应用场景

  • 根据不同的类型选择不同的计费策略、处理机制

参考:https://blog.csdn.net/u010247622/article/details/106220318/

  • 容错恢复机制,当程序出现错误时,使用备选方案

参考:https://www.cnblogs.com/LoveShare/p/10953940.html

算法结构

在这里插入图片描述

如图,该算法,一般先定义算法接口,然后根据不同的算法策略实现具体的算法;然后将算法注入到StrategyContext中,之后Client根据不同的类型获取对应的算法,执行之。

使用举例

公交计费时可以使用多种方式,常见的有现金、公交卡、电子公交卡、NFC等,每种方式计价的方式不同,比如:

  • 现金不打折
  • 公交卡打85折
  • 电子公交卡打9折
  • NFC打8折

现在需要实现公交计费。

典型的方式是使用if else判断不同场景进行计算,但是扩展性差,代码也不美观。

现在采用自定义注解结合策略模式实现计费,代码接口如下:

在这里插入图片描述

首先定义个枚举类,列举主要的业务类型

public enum CalculateTypeEnum {NFC("nfc"),CARD("card"),E_CARD("eCard"),CASH("cash");private String type;CalculateTypeEnum(String type){this.type=type;}public String getType(){return type;}
}

然后自定义一个注解@CalculateStrategy,该注解主要用于后面的具体算法上,作为标识使用

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CalculateStrategy {CalculateTypeEnum value() default CalculateTypeEnum.CASH;
}

实现策略模式最主要的是算法之间具有可替换性,这样的话需要定义算法接口,具体的算法实现了相同的接口,就具备了可替换性,接口定义如下:

public interface ICalculateStrategy {Double calculateAmount(Double oriAmount);
}

分别先现金、实体公交卡、电子公交卡、NFC的计算策略

@Service
@Slf4j
@CalculateStrategy(CalculateTypeEnum.CASH)
public class CashStrategy implements ICalculateStrategy{@Overridepublic Double calculateAmount(Double oriAmount) {log.info("Using CashStrategy");return oriAmount;}
}
@Service
@Slf4j
@CalculateStrategy(CalculateTypeEnum.CARD)
public class CardStrategy implements ICalculateStrategy{@Overridepublic Double calculateAmount(Double oriAmount) {log.info("Using CardStrategy");return oriAmount*0.9;}
}
@Service
@Slf4j
@CalculateStrategy(CalculateTypeEnum.E_CARD)
public class ECardStrategy implements ICalculateStrategy {@Overridepublic Double calculateAmount(Double oriAmount) {log.info("Using ECardStrategy");return oriAmount * 0.85;}
}
@Service
@Slf4j
@CalculateStrategy(CalculateTypeEnum.NFC)
public class NFCStrategy implements ICalculateStrategy{@Overridepublic Double calculateAmount(Double oriAmount) {log.info("Using NFCStrategy");return oriAmount*0.8;}
}

可以看到上面通过@Service注解将算法注入到IOC容器,使用@CalculateStrategy注解标识了不同类型的计费算法策略,现在需要将策略统一放在一个上下文中进行管理

@Component
public class CalculateStrategyContext implements InitializingBean {/*** 算法策略map*/private static Map<String, Class<ICalculateStrategy>> strategyMap;/*** 算法策略注入map** @throws Exception 异常*/@Overridepublic void afterPropertiesSet() throws Exception {//从IOC中获取所有加了@CalculateStrategy注解的类Map<String, Object> beansMap = AppContextUtil.appContext.getBeansWithAnnotation(CalculateStrategy.class);// 将其按照注解上的type放入map中beansMap.forEach((k, v) -> {Class<ICalculateStrategy> strategyClass = (Class<ICalculateStrategy>) v.getClass();String type = strategyClass.getAnnotation(CalculateStrategy.class).value().getType();if (Objects.isNull(CalculateStrategyContext.strategyMap)) {CalculateStrategyContext.strategyMap = new HashMap<>();}CalculateStrategyContext.strategyMap.put(type, strategyClass);});}/*** 根据类型获取策略** @param type 类型* @return 策略*/public static ICalculateStrategy getStrategyByType(String type) {Class<ICalculateStrategy> strategyClass = strategyMap.get(type);if (Objects.isNull(strategyClass)) {throw new RuntimeException("No strategy exists for " + type);}return (ICalculateStrategy) AppContextUtil.getBean(strategyClass);}
}

工具类AppContextUtil

@Component
public class AppContextUtil implements ApplicationContextAware {/*** 应用上下文*/public static ApplicationContext appContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {AppContextUtil.appContext = applicationContext;}public static Object getBean(Class<?> clz) {return appContext.getBean(clz);}
}

最后编写测试用例测试

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyBatisApplication.class)
public class CalculateStrategyContextTest {@Beforepublic void before() throws Exception {}@Afterpublic void after() throws Exception {}/*** Method: getStrategyByType(String type)*/@Testpublic void testGetStrategyByType() throws Exception {Double amount = 1000d;ICalculateStrategy cashStrategy = CalculateStrategyContext.getStrategyByType(CalculateTypeEnum.CASH.getType());System.out.println("cashStrategy:" + cashStrategy.calculateAmount(amount));ICalculateStrategy cardStrategy = CalculateStrategyContext.getStrategyByType(CalculateTypeEnum.CARD.getType());System.out.println("cardStrategy:" + cardStrategy.calculateAmount(amount));ICalculateStrategy eCardStrategy = CalculateStrategyContext.getStrategyByType(CalculateTypeEnum.E_CARD.getType());System.out.println("eCardStrategy:" + eCardStrategy.calculateAmount(amount));ICalculateStrategy nfcStrategy = CalculateStrategyContext.getStrategyByType(CalculateTypeEnum.NFC.getType());System.out.println("nfcStrategy:" + nfcStrategy.calculateAmount(amount));}} 

运行结果

在这里插入图片描述


http://chatgpt.dhexx.cn/article/YqyryqEb.shtml

相关文章

Option Explicit的作用

在VBScript中经常可以看到代码的最上面会出现“Option Explicit”&#xff0c;那么它的作用到底是干什么的呢&#xff1f; VBScript中并不要求显示定义变量&#xff0c;即变量可以不经定义直接使用&#xff0c;这样很方便&#xff0c;但是也很容易出现问题&#xff1b; 比如先定…

策略模式(常用用法)

策略模式 在软件开发中常常遇到类似的情况&#xff0c;当实现某一个功能存在多种算法或者策略&#xff0c;我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能&#xff0c;如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。 策略&#xff08;St…

策略模式之配置策略

了解策略模式建议大家先看一下这篇 https://www.runoob.com/design-pattern/strategy-pattern.html 步骤一创建一个接口 public interface Strategy {public int love(int a,int b); } 然后实现对应的接口A和B public class StrategyA implements Strategy {Overridepubli…

策略模式--strategy

策略模式 含义: 策略模式&#xff08;Strategy&#xff09;属于对象行为型设计模式&#xff0c; 1.主要是定义一系列的算法&#xff0c; 2.把这些算法一个个封装成拥有共同接口的单独的类&#xff0c;并且使它们之间可以互换。 策略模式使这些算法在客户端调用它们的时候能够互…

策略模式(Strategy)

设计模式系列 Strategy 策略模式——对象行为模式 1.意图 定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可相互替换。本模式使得算法可独立使用它的客户而变化。 2.适用性 当存在以下情况时使用Strategy模式 许多相关的类仅仅是行为有异。“策…

CTA 策略分享之三 -- 策略优化

上一个帖子介绍了一个趋势跟踪策略的优化思路&#xff0c;今天我们继续对策略进行分析&#xff0c;找到另外的优化方法。先看回测的权益曲线&#xff1a; 看到在2017 8月份到2018 2月份策略出现了较大的回撤。先定性分析一下&#xff0c;应该是在这段时间内日线级别的图形上震…

证书扩展中的oid

4.1.2.9. 扩展 该字段必须仅在版本为 3 时出现&#xff08;第 4.1.2.1 节&#xff09;。 如果存在&#xff0c;该字段是一个或多个证书扩展的序列。 Internet PKI 中证书扩展的格式和内容在第 4.2 节中定义。 为 X.509 v3 证书定义的扩展提供了将附加属性与用户或公钥相关联以及…

OID科普:物联网OID与互联网域名的区别 | 圣笛数控

1.主导机构不同&#xff0c;物联网OID是以全球共同参与的国际标准组织统一分配。各国管理自己境内部分&#xff0c;各国之间又可以互通。安全机制比互联网域名更高&#xff0c;既有高度自治又有全球互通。中国是整个体系中最大的支持者和应用者。 2.表现形式不同&#xff0c;物…

OID技术与物联网、区块链、大数据的关系 | OID科普

一、什么是物联网&#xff1f; 物联网既是网络与物品与企业与人与万物互联&#xff0c;更是信息技术业务和应用。是利用局部网络或互联网等通信技术把传感器、控制器、机器、人员和物等通过新的方式联在一起&#xff0c;形成人与物、物与物相联&#xff0c;实现信息化、远程管…

ArcGIS中ObjectID,FID和OID字段区别

ArcGIS中ObjectID&#xff0c;FID和OID字段区别 当我们创建一个Shapefiles&#xff08;shp&#xff09;, geodatabase feature classes&#xff08;gdb中的要素类&#xff09;或独立的dBase表&#xff08;dbf&#xff09;时&#xff0c;ArcGIS会自动为上述三种格式创建一个ID字…

圣笛数控|OID是什么?看OID在未来人类社会生活中的地位

OID定义&#xff0c;OID是什么&#xff1f; OID&#xff08;Object Identifier 对象标识符&#xff09;&#xff0c;是由国际三大标准组织ISO、IEC、ITU向全球颁布并供各国共同遵守的物品身份标识标准体系&#xff0c;现已被208个国家和地区采用。OID作为全球新物联通识标准&a…

OID科普:OID为什么被称为元标识?

物联网的标识有很多种&#xff0c;包括应用标识、载体标识以及网络标识&#xff0c;我们常见的 IC 卡、二维码、RFID 及传感器都可归为载体标识。当需要用到不同体系下的标识符时&#xff0c;如何处理不同标识符之间的兼容性等问题呢&#xff1f;这个时候&#xff0c;三大国际组…

SIFT+RANSAC做图像矫正

做图像矫正时使用了一下sift算法&#xff0c;尽管sift确实很牛&#xff0c;但还是会出现一些误匹配&#xff0c;直接计算两张影像的单应矩阵会出现很大误差&#xff0c;因此可以在计算时使用RANSAC算法在单应矩 阵的约束下剔除误匹配&#xff0c;并计算单应矩阵&#xff0c;基…

opencv沿斜线切割图片

opencv沿斜线切割图片 opencv如果沿水平或者数值的线来切割图片&#xff0c;是很简单的。但是&#xff0c;如果沿着斜线&#xff0c;那么就不能直接进行切割了。 我的思路是&#xff0c;根据直线上两点来确定直线方程&#xff0c;然后&#xff0c;逐个遍历图像中元素&#xf…

mean shift 图像分割(三)

Reference: [1] Mean shift: A robust approach toward feature space analysis, PAMI, 2002 [2] mean shift,非常好的ppt &#xff0c;百度文库链接 [3] Pattern Recognition and Machine Learning, Bishop, 2006&#xff0c;Sec 2.5 [4] Computer Vision Algorithms and App…

mean shift 图像分割(一、二、三)

MeanShift图像分割算法&#xff1a;大概是将复杂的背景&#xff0c;通过粗化提取整体信息&#xff0c;进而将图像分割。 接下来我想&#xff0c;将会抽出一部分时间&#xff0c;研究一下这个算法&#xff0c;以最终实现手势形状提取。 《Mean Shift: A Robust Approach Towar…

OpenCV在图像上画正弦曲线(c++)

写在前面&#xff1a;欢迎来到「湫歌」的博客。我是秋秋&#xff0c;一名普通的在校大学生。在学习之余&#xff0c;用博客来记录我学习过程中的点点滴滴&#xff0c;也希望我的博客能够更给同样热爱学习热爱技术的你们带来收获&#xff01;希望大家多多关照&#xff0c;我们一…

基于mean shift的图像分割

1 mean shift算法 Mean Shift 这个概念最早是由Fukunaga 等人[1]于1975 年在一篇关于概率密度梯度函数的估计中提出来的,其最初含义正如其名,就是偏移的均值向量,在这里Mean Shift 是一个名词,它指代的是一个向量,但随着Mean Shift 理论的发展,Mean Shift 的含义也发生了变化,如…

OpenCV对图像进行切边

1、概述 案例&#xff1a;使用OpenCV对旋转图片及正常图片进行切边。 A&#xff1a;对正常图片切边的步骤 1.加载图像 2.对图像进行灰度化 3.边缘检测 4.轮廓发现 5.找出符合目标的最大外接矩形&#xff0c;并使用矩形的四个坐标点绘制线 6.根据找到Rect在原图上切除ROI区域 7.…