Java设计模式:四、行为型模式-08:策略模式

文章目录

  • 一、定义:策略模式
  • 二、模拟场景:策略模式
  • 三、违背方案:策略模式
    • 3.0 引入依赖
    • 3.1 工程结构
    • 3.2 优惠券折扣计算类
    • 3.3 单元测试
  • 四、改善代码:策略模式
    • 4.1 工程结构
    • 4.2 策略模式结构图
    • 4.3 优惠券折扣实现
      • 4.3.1 定义优惠券接口
      • 4.3.2 满减优惠券接口实现
      • 4.3.3 直减优惠券接口实现
      • 4.3.4 折扣优惠券接口实现
      • 4.3.5 n元购优惠券接口实现
      • 4.3.6 策略控制类
    • 4.4 单元测试
      • 4.4.1 直减券测试
      • 4.4.2 满减券测试
      • 4.4.3 折扣券测试
      • 4.4.4 n元购测试
  • 五、总结:策略模式

一、定义:策略模式

请添加图片描述

  • 策略模式是具有同类可替代的行为逻辑算法场景。比如:
    • 不同类型的交易方式(信用卡、支付宝、微信)。
    • 生成唯一 ID 策略( UUIDDB自增DB+Redis雪花算法Leaf算法)等。

二、模拟场景:策略模式

请添加图片描述

  • 模拟在购买商品时使用的各种类型优惠券(满减、直减、折扣、m元)。
  • 这个场景几乎也是大家的一个日常购物省钱渠道,购买商品的时候都希望找一些优惠券,让购买的商品更加实惠。而且到了大促的时候就会有更多的优惠券需要计算那些商品一起购买更加优惠。
  • 这样的场景有时候用户用起来还是蛮爽的,但是最初这样功能的设定以及产品的不断迭代,对于程序员开发还是不容易的。
    • 因为这里包括了很多的规则和优惠逻辑,所以我们模拟其中的一个计算优惠的方式,使用策略模式来实现。

三、违背方案:策略模式

📖 对于优惠券的设计最初可能非常简单,就是一个金额的折扣,也没有现在这么多种类型。
所以如果没有这样场景的经验,往往设计上也是非常简单的。
但随着产品功能的不断迭代,如果程序最初设计的不具备很好的扩展性,那么往后就会越来越混乱。

3.0 引入依赖

<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><!-- LOGGING begin --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.5</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.0.9</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency>
</dependencies>

3.1 工程结构

design-21.0-1
|——src|——main|--java|--com.lino.design|-CouponDiscountService.java|--test|--com.lino.design.test|-ApiTest.java

3.2 优惠券折扣计算类

CouponDiscountService.java

package com.lino.design;/*** @description: 优惠券折扣计算接口*/
public class CouponDiscountService {/*** 计算优惠券折扣** @param type        优惠券类型:1-直减券,2-满减券,3-折扣券,4-n元购* @param typeContent 折扣价格* @param skuPrice    商品价格* @param typeExt     满减价格* @return 折扣后的价格*/public double discountAmount(int type, double typeContent, double skuPrice, double typeExt) {// 1.直减券if (1 == type) {return skuPrice - typeContent;}// 2.满减券if (2 == type) {if (skuPrice < typeExt) {return skuPrice;}return skuPrice - typeContent;}// 3.折扣券if (3 == type) {return skuPrice * typeContent;}// 4.n元购if (4 == type) {return typeContent;}return 0D;}
}
  • 以上是不同类型的优惠券计算折扣后的实际金额。
  • 入参包括:优惠券类型、优惠券金额、商品金额、满减金额
    • 因为有些优惠券是满多少减少多少,所以增加了 typeExt 类型,这也是方法的不好扩展性问题。
  • 最后是整个方法体中对优惠券折扣金额的实现,最开始可能是一个最简单的优惠券,后面随着产品功能的增加,不断的扩展 if 语句。

3.3 单元测试

ApiTest.java

package com.lino.design.test;import com.lino.design.CouponDiscountService;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test() {CouponDiscountService couponDiscountService = new CouponDiscountService();double result1 = couponDiscountService.discountAmount(1, 10D, 100D, 0D);logger.info("测试结果:直减优惠后金额:{}", result1);double result2 = couponDiscountService.discountAmount(2, 10D, 100D, 0D);logger.info("测试结果:满减优惠后金额:{}", result2);double result3 = couponDiscountService.discountAmount(3, 0.9D, 100D, 0D);logger.info("测试结果:折扣优惠后金额:{}", result3);double result4 = couponDiscountService.discountAmount(4, 90D, 100D, 0D);logger.info("测试结果:n元购金额:{}", result4);}
}

测试结果

17:05:07.040 [main] INFO  com.lino.design.test.ApiTest - 测试结果:直减优惠后金额:90.0
17:05:07.049 [main] INFO  com.lino.design.test.ApiTest - 测试结果:满减优惠后金额:90.0
17:05:07.049 [main] INFO  com.lino.design.test.ApiTest - 测试结果:折扣优惠后金额:90.0
17:05:07.049 [main] INFO  com.lino.design.test.ApiTest - 测试结果:n元购金额:90.0

四、改善代码:策略模式

💡 重构使用策略模式,优化代码结构,增强整体的扩展性。

4.1 工程结构

design-21.0-2
|——src|——main|--java|--com.lino.design|--impl|		|--MJCouponDiscount.java|		|--NYGCouponDiscount.java|		|--ZJCouponDiscount.java|		|--ZKCouponDiscount.java|-Context.java|-ICouponDiscount.java|--test|--com.lino.design.test|-ApiTest.java

4.2 策略模式结构图

请添加图片描述

  • 整体的结构模式并不复杂,主要体现的不同类型的优惠券在计算优惠券方式的不同计算策略。
  • 这里包括一个接口类(ICouponDiscount),以及四种优惠券类型的实现方式。
  • 最后提供了策略模式的上下控制类处理,整体的策略服务。

4.3 优惠券折扣实现

4.3.1 定义优惠券接口

ICouponDiscount.java

package com.lino.design;import java.math.BigDecimal;/*** @description: 优惠券折扣计算接口*/
public interface ICouponDiscount<T> {/*** 计算优惠券折扣** @param couponInfo 优惠券信息泛型* @param skuPrice   商品价格* @return 优惠券折扣后的价格*/BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
}
  • 定义了优惠券折扣接口,也增加了泛型用于不同类型的接口可以传递不同的类型参数。
  • 接口中包括商品金额以及出参返回最终折扣后的金额。

4.3.2 满减优惠券接口实现

MJCouponDiscount.java

package com.lino.design.impl;import com.lino.design.ICouponDiscount;
import java.math.BigDecimal;
import java.util.Map;/*** @description: 满减券*/
public class MJCouponDiscount implements ICouponDiscount<Map<String, String>> {/*** 满减计算* 1.判断满足x元后-n元,否则不减* 2.最低 支付金额1元** @param couponInfo 优惠券信息泛型* @param skuPrice   商品价格* @return 优惠后价格*/@Overridepublic BigDecimal discountAmount(Map<String, String> couponInfo, BigDecimal skuPrice) {String x = couponInfo.get("x");String n = couponInfo.get("n");// 小于商品金额条件的,直接返回商品原价if (skuPrice.compareTo(new BigDecimal(x)) < 0) {return skuPrice;}// 减去优惠金额判断BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(n));if (discountAmount.compareTo(BigDecimal.ZERO) < 1) {return BigDecimal.ONE;}return discountAmount;}
}

4.3.3 直减优惠券接口实现

ZJCouponDiscount.java

package com.lino.design.impl;import com.lino.design.ICouponDiscount;
import java.math.BigDecimal;/*** @description: 直减券*/
public class ZJCouponDiscount implements ICouponDiscount<Double> {/*** 直减计算* 1.使用商品价格减去优惠价格* 2.最低支付金额1元** @param couponInfo 优惠券信息泛型* @param skuPrice   商品价格* @return 优惠后价格*/@Overridepublic BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));if (discountAmount.compareTo(BigDecimal.ZERO) < 1) {return BigDecimal.ONE;}return discountAmount;}
}

4.3.4 折扣优惠券接口实现

ZKCouponDiscount.java

package com.lino.design.impl;import com.lino.design.ICouponDiscount;
import java.math.BigDecimal;/*** @description: 折扣券*/
public class ZKCouponDiscount implements ICouponDiscount<Double> {/*** 折扣计算* 1.使用商品价格乘以折扣比例,为最后支付金额* 2.保留两位小数* 3.最低支付金额1元** @param couponInfo 优惠券信息泛型* @param skuPrice   商品价格* @return 优惠后价格*/@Overridepublic BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);if (discountAmount.compareTo(BigDecimal.ZERO) < 1) {return BigDecimal.ONE;}return discountAmount;}
}

4.3.5 n元购优惠券接口实现

NYGCouponDiscount.java

package com.lino.design.impl;import com.lino.design.ICouponDiscount;
import java.math.BigDecimal;
import java.util.Map;/*** @description: n元购*/
public class NYGCouponDiscount implements ICouponDiscount<Double> {/*** n元购* 1.无论原价多少钱都固定金额购买** @param couponInfo 优惠券信息泛型* @param skuPrice   商品价格* @return 优惠后价格*/@Overridepublic BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {return new BigDecimal(couponInfo);}
}

4.3.6 策略控制类

Context.java

package com.lino.design;import java.math.BigDecimal;/*** @description: 策略控制类*/
public class Context<T> {private ICouponDiscount<T> couponDiscount;public Context(ICouponDiscount<T> couponDiscount) {this.couponDiscount = couponDiscount;}/*** 计算优惠券折扣** @param couponInfo 优惠券信息泛型* @param skuPrice   商品价格* @return 优惠券折扣后的价格*/public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {return couponDiscount.discountAmount(couponInfo, skuPrice);}
}
  • 策略模式的控制类主要是外部可以传递不同的策略实现,再通过统一的方法执行优惠策略计算。
  • 另外这里也可以包装出 map 结构,让外部只需要对应的泛型类型即可使用相应的服务。

4.4 单元测试

4.4.1 直减券测试

ApiTest.java

@Test
public void test_zj() {Context<Double> context = new Context<>(new ZJCouponDiscount());BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100));logger.info("测试结果:直减优惠后金额:{}", discountAmount);
}

测试结果

17:16:00.390 [main] INFO  com.lino.design.test.ApiTest - 测试结果:直减优惠后金额:90

4.4.2 满减券测试

ApiTest.java

@Test
public void test_mj() {Context<Map<String, String>> context = new Context<>(new MJCouponDiscount());Map<String, String> mapReq = new HashMap<>();mapReq.put("x", "100");mapReq.put("n", "10");BigDecimal discountAmount = context.discountAmount(mapReq, new BigDecimal(100));logger.info("测试结果:满减优惠后金额:{}", discountAmount);
}

测试结果

17:16:35.300 [main] INFO  com.lino.design.test.ApiTest - 测试结果:满减优惠后金额:90

4.4.3 折扣券测试

ApiTest.java

@Test
public void test_zk() {Context<Double> context = new Context<>(new ZKCouponDiscount());BigDecimal discountAmount = context.discountAmount(0.9D, new BigDecimal(100));logger.info("测试结果:折扣9折后金额:{}", discountAmount);
}

测试结果

17:17:06.907 [main] INFO  com.lino.design.test.ApiTest - 测试结果:折扣9折后金额:90.00

4.4.4 n元购测试

ApiTest.java

@Test
public void test_nyg() {Context<Double> context = new Context<>(new NYGCouponDiscount());BigDecimal discountAmount = context.discountAmount(90D, new BigDecimal(100));logger.info("测试结果:n元购优惠后金额:{}", discountAmount);
}

测试结果

17:17:35.616 [main] INFO  com.lino.design.test.ApiTest - 测试结果:n元购优惠后金额:90

💡 以上四个测试分别验证了不同类型优惠券的优惠策略,测试结果是满足我们的预期。
这里四种优惠券最终都是再原价 100 元上折扣 10 元,最终支付 90 元。

五、总结:策略模式

  • 策略模式案例相对来说并不复杂,主要的逻辑都是体现在关于不同类型优惠券的计算折扣策略上。
    • 结构相对来说也比较简单,在实际的开发中这样的设计模式也是非常常用的。
    • 另外策略模式与命令模式、适配器模式结构相似,但是思路是有差异的。
  • 通过策略模式的使用可以把我们方法中的 if 语句优化掉,大量的 if 语句使用会让代码难为扩展,也不好维护,同时在后期遇到各种问题也很难维护。
  • 在使用策略模式可以很好的满足隔离性和扩展性,对于不断新增的需求也非常方便承接。
  • 策略模式适配器模式组合模式 等,在一些结构上是比较相似的。但是每一个模式都有自己的逻辑特点,在使用的过程中最佳的方式是经过较多的实践来吸取经验,为后续的研发设计提供更好的技术输出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/98399.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】剑指 Offer <二刷>(7)

目录 题目&#xff1a;剑指 Offer 14- I. 剪绳子 - 力扣&#xff08;LeetCode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 题目&#xff1a;剑指 Offer 14- II. 剪绳子 II - 力扣&#xff08;…

selenium中定位shadow-root,以及获取shadow-root内部的数据

通过shadow-root的父级定位到shadow-root,再通过语句进行操作 两种方法&#xff1a; 第一种&#xff0c;Python种JS实现 第二种&#xff0c;selenium实现 1.0 案例网站 参考某橘色网站 2.0 js语句定位 可在控制台进行测试 测试语句 document.querySelector("ali-ba…

uni-app点击复制指定内容(点击复制)

官方api uni.setClipboardData(OBJECT) uni.setClipboardData({data: 要被复制的内容,success: function () {console.log(success);} });

文件上传漏洞全面渗透姿势

0x00 文件上传场景 (本文档只做技术交流) 文件上传的场景真的随处可见&#xff0c;不加防范小心&#xff0c;容易造成漏洞&#xff0c;造成信息泄露&#xff0c;甚至更为严重的灾难。 比如某博客网站评论编辑模块&#xff0c;右上角就有支持上传图片的功能&#xff0c;提交带…

.net core 上传文件大小限制

微软官网文档中给的解释是.net core 默认上传文件大小限制是30M&#xff0c;所以即便你项目里没有限制&#xff0c;这里也有个默认限制。 官网链接地址 总结了一下解决办法&#xff1a; 1.首先项目里添加一个web.config自定义配置文件 在配置文件中加上这段配置 <!--//…

微服务-kubernetes安装

文章目录 一、前言二、kubernetes2.1、Kubernetes (K8S) 是什么2.1.1、主要特性&#xff1a;2.2.2、传统部署方式&#xff1a;2.2.3、虚拟机部署2.2.4容器部署2.2.5什么时候需要 Kubernetes2.2.6、Kubernetes 集群架构 三、kubernetes安装3.1、主节点需要组件3.1.1、设置对应主…

Linux的服务器日志分析及性能调优

作为网络安全和数据传输的重要环节&#xff0c;代理服务器在现代互联网中扮演着至关重要的角色。然而&#xff0c;在高负载情况下&#xff0c;代理服务器可能面临性能瓶颈和效率问题。本文将介绍如何利用Linux系统对代理服务器进行日志分析&#xff0c;并提供一些实用技巧来优化…

CSS中如何隐藏元素但保留其占位空间(display:nonevsvisibility:hidden)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 隐藏元素但保留占位空间⭐ display: none;⭐ visibility: hidden;⭐ 总结⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&a…

算法笔记 二叉搜索树

二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称 BST&#xff09;是一种数据结构&#xff0c;用于存储具有可比较键&#xff08;通常是数字或字符串&#xff09;的元素 1 结构特点 节点结构&#xff1a;每个节点都有一个键和两个子节点&#xff08;左子节点和右子…

HTML5-4-表单

文章目录 表单属性表单标签输入元素文本域&#xff08;Text Fields&#xff09;密码字段单选按钮&#xff08;Radio Buttons&#xff09;复选框&#xff08;Checkboxes&#xff09;按钮&#xff08;button&#xff09;提交按钮(Submit)label标签 文本框&#xff08;textarea&am…

在ubuntu上安装ns2和nam(ubuntu16.04)

在ubuntu上安装ns2和nam 版本选择安装ns2安装nam 版本选择 首先&#xff0c;版本的合理选择可以让我们避免很多麻烦 经过测试&#xff0c;ubuntu的版本选择为ubuntu16.04&#xff0c;ns2的版本选择为ns-2.35&#xff0c;nam包含于ns2 资源链接(百度网盘) 链接:https://pan.bai…

DR IP-SoC China 2023 Day演讲预告 | 龙智Perforce专家解析芯片开发中的数字资产管理

2023年9月6日&#xff08;周三&#xff09;&#xff0c;龙智即将亮相于上海举行的D&R IP-SoC China 2023 Day&#xff0c;呈现集成了Perforce与Atlassian产品的芯片开发解决方案&#xff0c;助力企业更好、更快地进行芯片开发。 D&R IP-SoC China 2023 Day 是中国首个…