前言

在 设计模式(一)策略模式 —— 策略模式结构 和 设计模式(二)策略模式 —— 在程序中通过枚举使用策略模式 两篇博文中分析了策略模式的基础使用,在实际的项目开发中要结合spring容器使用策略模式,这篇博文以电商优惠券处理为例演示在spring中优雅的使用策略模式。

场景分析

比如在电商系统中,对各种类型的优惠券优惠金额的算法处理。如果直接写代码可能就出现了下面这段代码一样

    public Object couponTypeHandler(String couponType){if ("抵扣券".equals(couponType)){// doSomething}else if ("满减券".equals(couponType)){// doSomething}else if ("折扣券".equals(couponType)){// doSomething}else {// doSomething}return new Object();}

如果优惠券类型越来越多,这里的if...else或者switch的判断条件也会越来越多,后续对这个方法的维护也会越来越困难,也不符合开闭原则。

在spring中优雅的使用策略模式

定义优惠券相关基础类

定义Demo中使用的DTO对象和枚举对象

/*** 优惠券计算传输对象** @author lishuzhen* @createTime 2022年05月29日 22:23:00*/
public class CouponDTO {private CouponTypeEnum couponType;// other fieldpublic CouponTypeEnum getCouponType() {return couponType;}public void setCouponType(CouponTypeEnum couponType) {this.couponType = couponType;}
}/*** 优惠券类型枚举类** @author lishuzhen* @createTime 2022年05月29日 22:28:35*/
public enum CouponTypeEnum {DISCOUNT("折扣券"),REBATE("抵扣券"),FULL_REDUCE("满减券");private String name;CouponTypeEnum(String name) {this.name = name;}
}

定义策略接口

策略接口中包含两个方法的定义。

  • 定义当前策略类的适用的优惠券类型
  • 定义具体的计算策略交给子类实现。

/*** 优惠券策略接口** @author lishuzhen* @createTime 2022年05月29日 22:48:00*/
public interface CouponStrategy {/*** 适用的优惠券类型** @return*/CouponTypeEnum applyCouponType();/*** 优惠券策略算法执行入口** @param couponDTO* @return*/Object couponHandler(CouponDTO couponDTO);
}

定义优惠券策略计算的上下文对象

策略上下文对象主要完成两项操作

  • 应用启动时从spring容器中获取所有策略接口的实现类,注册到优惠券策略集合中
  • 提供对外暴露的优惠券计算接口,通过优惠券类型从优惠券策略集合中获取适用的策略类,委派具体的策略类完成计算逻辑。

/*** 优惠券策略上下文对象** @author lishuzhen* @createTime 2022年05月29日 22:58:00*/
@Component
public class CouponStrategyContext implements ApplicationContextAware {/*** 优惠券策略类集合*/private static Map<String, CouponStrategy> couponStrategyMap;/*** 策略上下文对象委派具体的策略执行算法** @param couponDTO* @return*/public static Object couponHandler(CouponDTO couponDTO) {return getCouponStrategy(couponDTO.getCouponType()).couponHandler(couponDTO);}/*** 获取适用的策略处理类** @param couponType* @return*/private static CouponStrategy getCouponStrategy(CouponTypeEnum couponType) {return Optional.ofNullable(couponStrategyMap.get(couponType.name())).orElseThrow(() -> new RuntimeException(String.format("not found coupon type strategy , coupon type is %s", couponType.name())));}/*** 从容器中加载所有优惠券策略接口的实现类,注册到优惠券策略集合中** @param applicationContext* @throws BeansException*/@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {Map<String, CouponStrategy> strategyBeans = applicationContext.getBeansOfType(CouponStrategy.class);if (strategyBeans == null || strategyBeans.size() == 0) {// 无可用的优惠券策略return;}couponStrategyMap = new HashMap<>(strategyBeans.size());for (CouponStrategy strategy : strategyBeans.values()) {couponStrategyMap.put(strategy.applyCouponType().name(), strategy);System.out.println("register strategy " + strategy.applyCouponType());}}
}

定义具体的策略实现类

策略实现类要完成三个操作

  • 实现applyCouponType方法,表明当前策略类适用的优惠券类型。供策略上下文对象中注册策略集合使用。
  • 实现couponHandler方法,完成自己的主要职责,对优惠券进行计算
  • 将自己注册到spring容器中

/*** 折扣券策略** @author lishuzhen* @createTime 2022年05月29日 23:01:00*/
@Service
public class DiscountCouponStrategy implements CouponStrategy {/*** 适用的优惠券类型** @return*/@Overridepublic CouponTypeEnum applyCouponType() {return CouponTypeEnum.DISCOUNT;}/*** 优惠券策略算法执行入口** @param couponDTO* @return*/@Overridepublic Object couponHandler(CouponDTO couponDTO) {System.out.println("执行折扣券策略");return null;}
}/*** 满减券计算策略** @author lishuzhen* @createTime 2022年05月29日 23:01:55*/
@Service
public class FullReduceCouponStrategy implements CouponStrategy {/*** 适用的优惠券类型** @return*/@Overridepublic CouponTypeEnum applyCouponType() {return CouponTypeEnum.FULL_REDUCE;}/*** 优惠券策略算法执行入口** @param couponDTO* @return*/@Overridepublic Object couponHandler(CouponDTO couponDTO) {System.out.println("执行满减券策略");return null;}
}/*** 抵扣券计算策略** @author lishuzhen* @createTime 2022年05月29日 23:02:40*/
@Service
public class RebateCouponStrategy implements CouponStrategy {/*** 适用的优惠券类型** @return*/@Overridepublic CouponTypeEnum applyCouponType() {return CouponTypeEnum.REBATE;}/*** 优惠券策略算法执行入口** @param couponDTO* @return*/@Overridepublic Object couponHandler(CouponDTO couponDTO) {System.out.println("执行抵扣券策略");return null;}
}

调用方代码

调用只需要调用策略上下文对象即可。

        // 模拟业务参数中的优惠券类型CouponDTO couponDTO = new CouponDTO();couponDTO.setCouponType(CouponTypeEnum.REBATE);// 委派优惠券策略上下文对象对优惠券进行计算CouponStrategyContext.couponHandler(couponDTO);

启动应用

注册优惠券策略日志

日志中可以看到,在CouponStrategyContext的setApplicationContext方法中打印的注册日志已经打印到控制台,说明注册优惠券策略集合已完成。

23:51:36.505 [restartedMain] INFO  o.a.c.c.StandardEngine - [log,173] - Starting Servlet engine: [Apache Tomcat/9.0.41]
23:51:36.584 [restartedMain] INFO  o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring embedded WebApplicationContext
register strategy DISCOUNT
register strategy FULL_REDUCE
register strategy REBATE

调用策略上下文对象,模拟业务场景

在模拟调用方的代码中写了CouponTypeEnum.REBATE类型,控制台正常打印了“执行抵扣券策略”,说明已经匹配到了具体的策略类。

23:55:06.333 [http-nio-8080-exec-1] INFO  o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
执行抵扣券策略

总结

优点

  • 利用Spring容器IOC和DI的特性,简化了手动创建策略类实例的过程。
  • 调用方无需知道具体的策略类,降低模块间的耦合。
  • 方便扩展,只需要新增或者修改某一具体策略类即可,完全符合开闭原则。

缺点

  • 暂时没找到缺点...

设计模式(三)策略模式——在Spring中使用策略模式相关推荐

  1. java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)

    java中的静态.动态代理模式以及Spring中的CgLib动态代理解读(面试必问) 静态代理 动态代理 CgLib动态代理     基础知: 反射知识 代理(Proxy)是一种设计模式,提供了对目标 ...

  2. Spring中的代理模式

    Spring中的代理模式,是Spring中一大核心 AOP(切面编程)的底层实现. 代理模式可分为: 静态代理 动态代理 静态代理 涉及三类角色,简单分析一下: 抽象角色(一般会使用接口或抽象类解决) ...

  3. Spring中的模板模式

    文章目录 前言 一.模板模式优缺点? 二.代码例子 前言 所谓模板,即定义了一套标准版的骨架,我们需要做的就是在此基础上不断演化新的版本,已达到设计需要.如果要以开发者的编程思维角度去思考这个设计方式 ...

  4. 重温经典之《企业应用架构模式》——.NET中的架构模式运用 (Base Patterns 1)

    今天看看几个基本模式,这包括Gateway模式,Mapper模式,LayerSupertype模式和Separated Interface模式. 在这本书的最后一章,Martin Fowler放了一大 ...

  5. ofb模式_密码学中的输出反馈模式(OFB)

    ofb模式 This is an output feedback (OFB) mode is similar in structure to that of CFB in Cryptography. ...

  6. python编程模式_Python编程中的反模式

    原标题:Python编程中的反模式 这篇文章收集了我在Python新手开发者写的代码中所见到的不规范但偶尔又很微妙的问题. 本文的目的是为了帮助那些新手开发者渡过写出丑陋的Python代码的阶段. 对 ...

  7. java repository模式_MVC架构中的Repository模式 个人理解

    个人理解:Repository是一个独立的层,介于领域层与数据映射层(数据访问层)之间.它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接口提供给领域层进行领域对象的访问.Reposit ...

  8. 如何开启电脑上帝模式_Windows 10中的“上帝模式”文件夹是什么,以及如何启用它?

    如何开启电脑上帝模式 What if Windows let you quickly access administrative tools, backup and restore options a ...

  9. 设计模式(十一):从文Finder中认识组合模式(Composite Pattern)

    上一篇博客中我们从从电影院中认识了"迭代器模式"(Iterator Pattern),今天我们就从文件系统中来认识一下"组合模式"(Composite Patt ...

最新文章

  1. OUYA游戏开发快速入门教程1.2OUYA的硬件规格
  2. IDEA上安装和使用checkstyle,findbugs,visualVM,PMD插件
  3. speex 编译(转)
  4. mysql截取字符串最后两位_Mysql字符串截取函数SUBSTRING的用法说明
  5. 为什么年轻一代 连操作系统的基本知识 都不懂?
  6. selenium环境搭建,浏览器驱动安装
  7. g2o图优化简介与基本使用方法
  8. mysql 单标递归_MYSQL递归树查询的实现
  9. C中无警告输出size_t的值
  10. mongodb详细优化策略方案
  11. linux显卡驱动安装在哪个文件夹,linux 下安装Nvidia显卡驱动
  12. 关于CWMP基础(一)----(TR069)
  13. Python_learn_飞机大战
  14. 数字特征值-对数字求特征值是常用的编码算法,奇偶特征是一种简单的特征值
  15. 存储、冯诺伊曼和哈佛结构之间的关系
  16. 将链接转成base64格式生成二维码和把页面生成图片
  17. vue使用 moment.js 格式化时间(获取当前日期的周一和周日)
  18. 解决电脑插耳机听视频暂停后继续耳机声音突然变大的问题(详细图解)
  19. python信息检索系统_GitHub - Uyouii/SearchingSystem: python实现的基于倒排索引和向量空间模型实现的信息检索系统...
  20. P1258 小车问题

热门文章

  1. 似物性检测——linux下运行objectness-v2.2并分析源码(pami2012论文)
  2. 在华为 Kylin V10 SP1操作系统,HUAWEI,Kunpeng 920 CPU(4Cores)单机上模拟部署生产环境TiDB集群...
  3. 【dva】dva使用与实现(七)
  4. Mysql 使用存储过程合并多个表数据
  5. 删除了大文件,但是磁盘并没有释放
  6. vue 可用的 ui_ui设计的10种可用性启发法
  7. python中if语句中可用break_python的if循环语句
  8. C/C++使用ODBC连接MSSQL数据库
  9. JS事件 失焦事件(onblur)onblur事件与onfocus是相对事件,当光标离开当前获得聚焦对象的时候,触发onblur事件,同时执行被调用的程序。...
  10. javax.crypto.Cipher类--加密和解密