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

news2025/1/12 20:53:55

文章目录

  • 一、定义:策略模式
  • 二、模拟场景:策略模式
  • 三、违背方案:策略模式
    • 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);

    @Test
    public 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 优惠后价格
     */
    @Override
    public 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 优惠后价格
     */
    @Override
    public 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 优惠后价格
     */
    @Override
    public 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 优惠后价格
     */
    @Override
    public 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.coloradmin.cn/o/961427.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Django+node.js+MySQL+杰卡德相似系数智能新闻推荐系统——机器学习算法应用(含Python全部工程源码)+数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境node.js前端环境MySQL数据库 模块实现1. 数据预处理2. 热度值计算3. 相似度计算1&#xff09;新闻分词处理2&#xff09;计算相似度 4. 新闻统计5. API接口开发6. 前端界面实现1&#xff09;运行逻辑2&#xff0…

IBM Spectrum LSF Application Center 以应用程序为中心的工作负载提交和管理

IBM Spectrum LSF Application Center 为集群用户和管理员提供了一个灵活的、以应用为中心的界面。IBM Spectrum LSF Application Center 作为 IBM Spectrum LSF 的可选附加模块提供&#xff0c;使用户能够与直观、自我记录的界面进行交互。这提高了用户满意度和生产力。通过对…

【100天精通python】Day50:python web编程_Django框架使用

目录 1 安装Django Web框架 2 创建一个Django 项目 3 数据模型 3.1 在应用程序的 models.py 文件中定义数据模 3.2 创建模型的迁移文件并应用 3.2.1 查询模型对象&#xff1a; 3.2.2 创建新模型对象&#xff1a; 3.2.3 更新模型对象&#xff1a; 3.2.4 删除模型对象&a…

Docker构建Springboot项目,并发布测试

把SpringBoot项目打包成Docker镜像有两种方案&#xff1a; 全自动化&#xff1a;先打好docker镜像仓库&#xff0c;然后在项目的maven配置中配置好仓库的地址&#xff0c;在项目里配置好Dockerfile文件&#xff0c;这样可以直接在idea中打包好后自动上传到镜像仓库&#xff0c…

jmeter 线程组

在jmeter中&#xff0c;通过指定并发数量、启动延迟时间和持续时间&#xff0c;并组织示例&#xff08;Samplers&#xff09;在多个线程之间的执行方式&#xff0c;实现模拟并发用户的行为。 添加线程组&#xff1a; 在测试计划中&#xff0c;右键点击“添加” -> “Thread…

spring高级源码50讲-37-42(springBoot)

Boot 37) Boot 骨架项目 如果是 linux 环境&#xff0c;用以下命令即可获取 spring boot 的骨架 pom.xml curl -G https://start.spring.io/pom.xml -d dependenciesweb,mysql,mybatis -o pom.xml也可以使用 Postman 等工具实现 若想获取更多用法&#xff0c;请参考 curl …

在kali环境下安装Beef-Xss靶场搭建

目录 一、更新安装包 二、安装beef-xss 三、启动Beef-Xss工具 1、查看hook.js 2、查看后台登录地址 3、查看用户名和登录密码 4、登录页面 5、点击 Hook me:将配置的页面导入BEEF中 一、更新安装包 ┌──(root㉿kali)-[/home/kali] └─# apt-get update 二、安装bee…

MySQL以及版本介绍

一、MySQL的介绍 MySQL数据库管理系统由瑞典的DataKonsultAB公司研发&#xff0c;该公司被Sun公司收购&#xff0c;现在Sun公司又被Oracle公司收购&#xff0c;因此MySQL目前属于 Oracle 旗下产品。 MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用…

Golang单元测试举例

1.第一个例子 cal.go package mainfunc addUpper(n int) int {res : 0for i : 1; i < n; i {res i}return res }func getSub(n1 int, n2 int) int {return n1 - n2 }cal_test.go package main//测试文件名必须是_test.go结尾 //测试函数必须Test开头 import ("fmt…

CAD怎么批量打印出来?学会这个方法快速打印

CAD文件是3D设计或2D图纸的数字版本&#xff0c;可以使用计算机软件创建和修改。如果遇到以下几种情况&#xff0c;我们可能需要将CAD文件打印出来&#xff1a; 1、制造和生产&#xff1a;CAD文件可以用于制造和生产物品&#xff0c;例如汽车零件、建筑工程、机械工具等。打印…

广电运营商三网融合监控运维方案

随着三网融合逐步发展、深化&#xff0c;广电网络从为用户提供原本单一的信息服务转向了集语音、文字、图像为一体的信息服务&#xff0c;同时也实现了由单一独立的网络向综合性网络的改变。如何在业务的融合与竞争中创造核心竞争力&#xff0c;利用自身网络覆盖率上的优势&…

电脑报错vcomp100.dll丢失怎样修复,多个解决方法分享

今天&#xff0c;我想和大家分享一下关于vcomp100.dll丢失修复的经验。在我们的日常生活中&#xff0c;电脑出现问题是在所难免的&#xff0c;而vcomp100.dll文件丢失的问题也是很常见的。那么&#xff0c;当遇到这个问题时&#xff0c;我们应该如何进行修复呢&#xff1f;接下…

pdf转word格式乱了怎么调整?学学这个转换方法

pdf转word格式乱了怎么调整&#xff1f;PDF文件通常不能编辑&#xff0c;这使得它们在需要修改或添加内容时变得不方便。因此&#xff0c;将PDF文件转换为Word文档可以使它们更容易编辑和更新。当pdf转换成word的时候&#xff0c;格式乱了的话&#xff0c;也可以直接进行调整。…

许战海咨询战略文库│确保战略成功:21世纪企业须建立竞争性组织

摘要&#xff1a;在21世纪激烈的市场竞争中,打造竞争性组织是解锁企战略成功的关键因素。邓小平的智慧名言“方向定了,干部是决定因素”也充分印证了这一点,建立一个适应新时代、新竞争环境的竞争性组织&#xff0c;企业才能在不断变化和发展的市场环境中立于不败之地。 “方向…

文件上传下载

文件上传下载 创建模块 web.xml <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-a…

基于侏儒猫鼬算法优化的BP神经网络(预测应用) - 附代码

基于侏儒猫鼬算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于侏儒猫鼬算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.侏儒猫鼬优化BP神经网络2.1 BP神经网络参数设置2.2 侏儒猫鼬算法应用 4.测试结果&#xff1a;5…

【经验分享】Markdown中如何显示空格和回车

Markdown中如何显示空格和回车 空格 利用html中的空格实体引用&#xff1a; eg&#xff1a; 这是一些 额外的空格。回车&#xff1a; 方法一&#xff1a;在你想要回车的地方连续按两次回车键 方法二&#xff1a;使用<br>标签 eg&#xff1a; 我想显示<br>…

【vue】盘点Vue2和Vue3的10种组件通信方式:

文章目录 一、介绍:二、props【1】选项式API【2】组合式Api【3】setup语法糖 三、emit【1】选项式API【2】组合式Api【3】setup语法糖 四、attrs和listeners【1】选项式API【2】组合式API【3】setup语法糖 五、provide/inject【1】选项式API【2】组合式API【3】setup语法糖 六、…

Linux根目录的文件系统是如何被挂载的

第一步是先在内存中创建rootfs.这时rootfs只有 “/” 目录。 https://mp.weixin.qq.com/s?__bizMzUxNDUwOTc0Nw&mid2247484182&idx1&snc807877da1181fa97175c1a6602ad763&chksmf9459dcace3214dcee56f68ac03b8b377f4febf5fd8ca28e2ff9a403ac0940f91c6617338625…