当前位置: 首页 > news >正文

【设计模式】行为型设计模式之 策略模式学习实践

介绍

策略模式(Strategy),就是⼀个问题有多种解决⽅案,选择其中的⼀种使⽤,这种情况下我们
使⽤策略模式来实现灵活地选择,也能够⽅便地增加新的解决⽅案。⽐如做数学题,⼀个问题的
解法可能有多种;再⽐如商场的打折促销活动,打折⽅案也有很多种,有些商品是不参与折扣活
动要按照原价销售,有些商品打8.5折,有些打6折,有些是返现5元等。

优缺点和场景

优点

  1. 完美符合开闭原则,可以再不修改原系统基础上选择算法行为,或者新增新的算法。
  2. 策略模式,将一类算法进行了抽象,可以将公共部分进行抽离。避免重复代码。
  3. 策略模式对算法的封装,将算法的责任和算法本身分割开,交给不同的对象管理。提供了可替换继承关系的办法,如果不用策略模式,那么环境类,可能自己就有多个子类了,算法和算法的使用还在一起,那就不符合开闭原则。
  4. 可以避免多重条件选择语句。
  5. 算法抽离后,更方便不同的环境类复用。

缺点

  1. 客户端必须知道所有的策略类,并且决定使用哪一个。
  2. 造成了很多具体的策略的类,细小的变化都需要增加新的具体策略类。

使用场景

  1. 系统需要动态的在某些算法里进行选择,那么使用策略模式,用户只要维持一个算法的抽象类对象即可。
  2. 一个对象有很多的行为,避免在一个类里,根据不同的条件进行多重条件判断时。(if(a) 行为a if(b)行为b 将a和b拆成两个具体类。) 可以使用策略模式,将不同的行为,抽象成具体的策略类。
  3. 需要将具体的算法实现,和使用者进行解耦,提高算法的保密性和安全性。

结构

略模式对算法的封装,将算法的责任和算法本身分割开,交给不同的对象管理。使用算法的上下文环境类中,针对抽象的策略类进行编程。符合依赖倒转原则,并且出现了新的算法时,只需要增加一个新的实现即可。

  • **策略(Strategy) **定义所有⽀持算法的公共接⼝。 Context 使⽤这个接⼝来调⽤某 ConcreteStrategy 定义的算法。
  • **策略实现(ConcreteStrategy) **实现了Strategy 接⼝的具体算法
  • **上下⽂环境(Context) **维护⼀个 Strategy 对象的引⽤,⽤⼀个 ConcreteStrategy 对象来装配可定义⼀个接⼝⽅法让 Strategy 访问它的数据

UML类图

基础案例

针对不同商品的打折算法,在引入策略模式前,由一个算法类的方法维护,包含大量的条件转移,并且也不利于维护。
代码下载:strategy.zip

引入策略模式前

引入策略模式前,不同的打折算法的计算过程存在的问题。

  1. 有多重条件选择语句,代码混乱
  2. 不同的算法没有办法进行在别处复用
  3. 新增算法的话,需要修改原来的代码,不符合开闭原则。
package behavioralPattern.strategy;import java.text.MessageFormat;/*** 引入策略模式前,不同的打折算法的计算过程* 1.有多重条件选择语句,代码混乱* 2.不同的算法没有办法进行在别处复用* 3.新增算法的话,需要修改原来的代码,不符合开闭原则。** @author liuyp* @date 2022/09/25*/
public class BuyGoods {private String goods;private double price;private double finalPrice;private String desc;public BuyGoods(String goods, double price) {this.goods = goods;this.price = price;}public double calculate(String discountType) {if ("discount85".equals(discountType)) {finalPrice = price * 0.85;desc = "该商品可享受8.5折优惠";} else if ("discount6".equals(discountType)) {finalPrice = price * 0.6;desc = "该商品可享受6折优惠";} else if ("return5".equals(discountType)) {finalPrice = price >= 5 ? price - 5 : 0;desc = "该商品可返现5元";} else {finalPrice = price;desc = "对不起,该商品不参与优惠活动";}System.out.println(MessageFormat.format("您购买的商品为:{0},原价为: {1},{2},最终售卖价格为:{3}", goods, price, desc, finalPrice));return finalPrice;}
}

引入策略模式

修改步骤

  1. 策略: 引入抽象类,所有的价格计算算法实现该抽象类。
  2. 策略实现:针对原有的if else中的价格计算算法,分别在一个个具体的策略实现类中进行实现。
  3. 环境类:购买商品的类中,只需要维护一个策略抽象类的引用即可,传入不通风策略实现,即可实现不同的打折策略。

抽象打折策略

/*** 策略模式中,对策略的抽象层。* 抽象出了公共的描述、价格属性。* 定义了需要子类实现的,具体的打折策略方法。** @author StoneYu* @date 2022/09/25*/
public abstract class AbstractDiscount {protected double finalPrice;protected String desc;public AbstractDiscount(String desc) {this.desc = desc;}public double getFinalPrice() {return finalPrice;}public void setFinalPrice(double finalPrice) {this.finalPrice = finalPrice;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public abstract double discount(double price);
}

拆分各个打折策略的实现

public class Discount6 extends AbstractDiscount {public Discount6() {super("该商品可享受6折优惠");}@Overridepublic double discount(double price) {finalPrice = price * 0.6;return finalPrice;}
}public class Discount85 extends AbstractDiscount {public Discount85() {super("该商品可享受8.5折优惠");}@Overridepublic double discount(double price) {finalPrice = price * 0.85;return finalPrice;}
}public class NoDiscount extends AbstractDiscount {public NoDiscount() {super("对不起,该商品不参与优惠活动");}@Overridepublic double discount(double price) {finalPrice = price;return finalPrice;}
}public class Return5 extends AbstractDiscount {public Return5() {super("该商品可返现5元");}@Overridepublic double discount(double price) {this.finalPrice = price >= 5 ? price - 5 : 0;return finalPrice;}
}

修改环境类,只需要维护策略的引用

/*** 策略模式-环境类* 使用策略模式优化后的购买商品的方法* 1.没有了各种if-else* 2.不需要关注算法的具体实现,只需要维护一个策略的抽象类引用。符合依赖倒转原则** @author StoneYu* @date 2022/09/25*/
public class BuyGoods {private String goods;private double price;private AbstractDiscount abstractDiscount;public BuyGoods(String goods, double price, AbstractDiscountabstractDiscount) {this.goods = goods;this.price = price;this.abstractDiscount = abstractDiscount;}public double calculate() {double finalPrice = abstractDiscount.discount(this.price);String desc = abstractDiscount.getDesc();System.out.println(MessageFormat.format("商品:{0},原价:{1},{2},最 终价格为:{3}", goods, price, desc, finalPrice));return finalPrice;}
}

Spring中实践

新建策略接口

public interface DiscountStratege {/*** 折扣方法*/void discount();}

具体的策略 1

/*** 具体策略实现1** @author LiuYuping* @date 2024/03/14 15:14*/
@Component
public class FullReductionDiscountStratege implements DiscountStratege{@Overridepublic void discount() {System.out.println("满减策略,满一百减100");}
}

具体策略 2

/*** 具体策略实现2** @author LiuYuping* @date 2024/03/14 15:15*/
@Component
public class WeekDayDiscountStratege implements DiscountStratege{@Overridepublic void discount() {System.out.println("这里是周末满减策略");}
}

策略枚举,保存所有策略名称

public enum DiscountStrategeEnum {WEEK_DAY_STRATEGE("fullReductionDiscountStratege","满一百减一百"),FULL_REDUCTION("weekDayDiscountStratege","周末满减策略");String concernedStrategeBeanId;String strategeName;DiscountStrategeEnum(String concernedStrategeBeanId, String strategeName) {this.concernedStrategeBeanId = concernedStrategeBeanId;this.strategeName = strategeName;}public String getConcernedStrategeBeanId() {return concernedStrategeBeanId;}public String getStrategeName() {return strategeName;}
}

策略 Context 保存所有策略 Bean 示例

@Service
public class DiscountStrategeContext {@Autowiredprivate Map<String,DiscountStratege> allDiscountStrategeMap;/*** 获取指定的策略** @param discountStrategeEnum 折扣策略枚举* @return {@link DiscountStratege}*/public DiscountStratege getStratege(DiscountStrategeEnum discountStrategeEnum){return allDiscountStrategeMap.get(discountStrategeEnum.getConcernedStrategeBeanId());}}

用例

在需要使用策略的地方,按需注入指定类型的策略对象,新增策略时不需要修改原有代码

@SpringBootTest
class DemoApplicationTests {@AutowiredDiscountStrategeContext discountStrategeContext;@Testvoid testDiscountStratege() {DiscountStratege stratege = discountStrategeContext.getStratege(DiscountStrategeEnum.WEEK_DAY_STRATEGE);stratege.discount();}}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Vue】图形验证码功能
  • python类中常见的内置函数
  • 表的设计与查询
  • 豆瓣电影信息爬虫实战-2024年6月
  • 《Navi日语社》App:日语学习与翻译的智能助手,支持日文OCR识别提取文字,无需打字对着说话就能翻译的日语语音翻译工具!
  • 华为od-C卷100分题目-3用连续自然数之和来表达整数
  • Word中插入Mathtype右编号,调整公式与编号的位置
  • PHP维吾尔文转换成拉丁文
  • 前端JS必用工具【js-tool-big-box】学习,获取当前浏览器向上滚动还是向下滚动,获取当前距离顶部和底部的距离
  • vue-$set修改深层对象的值
  • 【云原生进阶之数据库技术】第二章-Oracle-原理-4.6.3.7-闪回事务查询技术
  • 我用香橙派做了一个Klipper 3D打印控制器
  • QuanTA: 一种新的高秩高效微调范式
  • 码蹄集部分题目(2024OJ赛18期;并查集+ST表+贪心)
  • GMT shp转gmt数据
  • 【刷算法】求1+2+3+...+n
  • Angular 响应式表单之下拉框
  • Computed property XXX was assigned to but it has no setter
  • ES6 ...操作符
  • GitUp, 你不可错过的秀外慧中的git工具
  • Making An Indicator With Pure CSS
  • node学习系列之简单文件上传
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • Web标准制定过程
  • 爱情 北京女病人
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 给Prometheus造假数据的方法
  • 关于List、List?、ListObject的区别
  • 如何利用MongoDB打造TOP榜小程序
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • const的用法,特别是用在函数前面与后面的区别
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • (1)Android开发优化---------UI优化
  • (42)STM32——LCD显示屏实验笔记
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (排序详解之 堆排序)
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • ****三次握手和四次挥手
  • .equals()到底是什么意思?
  • .Mobi域名介绍
  • .net 提取注释生成API文档 帮助文档
  • .Net 执行Linux下多行shell命令方法
  • .Net--CLS,CTS,CLI,BCL,FCL
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .NET应用UI框架DevExpress XAF v24.1 - 可用性进一步增强
  • /3GB和/USERVA开关
  • :中兴通讯为何成功