java设计模式之:策略模式

news2024/11/25 0:57:23

文章目录

  • 什么是策略模式?
  • 策略模式结构
  • 策略模式适用场景
  • 简单示例
  • 项目实战
    • 场景
    • 用一坨坨代码实现
    • 策略模式重构代码
    • 代码实现
      • 优惠券接口
      • 优惠券接口实现
      • 策略控制类
      • 测试类
  • 总结


设计模式是软件设计中常见问题的典型解决方案。 它们就像能根据需求进行调整的预制蓝图, 可用于解决代码中反复出现的设计问题。

今天就拿其中一个问题来分析,使用策略模式来解决问题,没有了解过策略模式或者长时间不用已经忘了策略模式的小伙伴先来简单了解一下策略模式吧。

什么是策略模式?

策略模式是一种行为型模式,它将对象和行为分开,将行为定义为 一个行为接口具体行为的实现。策略模式最大的特点是行为的变化,行为之间可以相互替换。每个if判断都可以理解为就是一个策略。本模式使得算法可独立于使用它的用户而变化。

简单理解就是,针对不同的场景,使用不同的策略进行处理。

策略模式结构

bcc4afc7056000dca2dabe16f60165a.png

  • Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。

  • Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。

策略模式适用场景

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们 的行为,那么使用策略模式可以动态地让一个对象在许多行 为中选择一种行为。
  • 一个系统需要动态地在几种算法中选择一种。
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行 为就只好使用多重的条件选择语句来实现。
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

生活中比较常见的应用模式有:

  1. 电商网站支付方式,一般分为银联、微信、支付宝,可以采用策略模式。
  2. 电商网站活动方式,一般分为满减送、限时折扣、包邮活动,拼团等可以采用策略模式。

简单示例

场景:最近太热了,想要降降温,有什么办法呢

首先,定义一个降温策略的接口

public interface CoolingStrategy {

    /**
     * 处理方式
     */
    void handle();

}

定义3种降温策略;实现策略接口

public class IceCoolingStrategy implements CoolingStrategy {
    @Override
    public void handle() {
        System.out.println("使用冰块降温");
    }
}
public class FanCoolingStrategy implements CoolingStrategy {

    @Override
    public void handle() {
        System.out.println("使用风扇降温");
    }
}
public class AirConditionerCoolingStrategy implements CoolingStrategy {
    @Override
    public void handle() {
        System.out.println("使用空调降温");
    }
}

定义一个降温策略的上下文

public class CoolingStrategyContext {

    private final CoolingStrategy strategy;

    public CoolingStrategyContext(CoolingStrategy strategy) {
        this.strategy = strategy;
    }

    public void coolingHandle() {
        strategy.handle();
    }

} 

测试

public class Main {
    public static void main(String[] args) {
        
        CoolingStrategyContext context = new CoolingStrategyContext(new FanCoolingStrategy());
        context.coolingHandle();

        context = new CoolingStrategyContext(new AirConditionerCoolingStrategy());
        context.coolingHandle();

        context = new CoolingStrategyContext(new IceCoolingStrategy());
        context.coolingHandle();
    }
} 

运行结果:

使用风扇降温 
使用空调降温 
使用冰块降温 

以上就是一个策略模式的简单实现

项目实战

场景

模拟在购买商品时候使用的各种类型优惠券(满减、直减、折扣、n元购)

这个场景几乎也是大家的一个日常购物省钱渠道,购买商品的时候都希望找一些优惠券,让购买的商品更加实惠。而且到了大促的时候就会有更多的优惠券需要计算那些商品一起购买更加优惠!

用一坨坨代码实现

/**
 * 优惠券折扣计算接口
 * <p>
 * 优惠券类型;
 * 1. 直减券
 * 2. 满减券
 * 3. 折扣券
 * 4. n元购
 */
public class CouponDiscountService {

    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语句。实际的代码可能要比这个多很多

策略模式重构代码

image.png

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

代码实现

优惠券接口

public interface ICouponDiscount<T> {

    /**
     * 优惠券金额计算
     * @param couponInfo 券折扣信息;直减、满减、折扣、N元购
     * @param skuPrice   sku金额
     * @return           优惠后金额
     */
    BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);

}
  • 定义了优惠券折扣接口,也增加了泛型用于不同类型的接口可以传递不同的类型参数。
  • 接口中包括商品金额以及出参返回最终折扣后的金额,这里在实际开发中会比现在的接口参数多一些,但核心逻辑是这些。

优惠券接口实现

满减

public class MJCouponDiscount implements ICouponDiscount<Map<String,String>>  {

    /**
     * 满减计算
     * 1. 判断满足x元后-n元,否则不减
     * 2. 最低支付金额1元
     */
    public BigDecimal discountAmount(Map<String,String> couponInfo, BigDecimal skuPrice) {
        String x = couponInfo.get("x");
        String o = couponInfo.get("n");

        // 小于商品金额条件的,直接返回商品原价
        if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;
        // 减去优惠金额判断
        BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
        if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;

        return discountAmount;
    }
}

直减

public class ZJCouponDiscount implements ICouponDiscount<Double>  {

    /**
     * 直减计算
     * 1. 使用商品价格减去优惠价格
     * 2. 最低支付金额1元
     */
    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;
    }

}

折扣

public class ZKCouponDiscount implements ICouponDiscount<Double> {


    /**
     * 折扣计算
     * 1. 使用商品价格乘以折扣比例,为最后支付金额
     * 2. 保留两位小数
     * 3. 最低支付金额1元
     */
    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;
    }

}

N元购

public class NYGCouponDiscount implements ICouponDiscount<Double> {

    /**
     * n元购购买
     * 1. 无论原价多少钱都固定金额购买
     */
    public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
        return new BigDecimal(couponInfo);
    }

}
  • 以上是四种不同类型的优惠券计算折扣金额的策略方式,可以从代码中看到每一种优惠方式的优惠金额。

策略控制类

public class Context<T> {

    private ICouponDiscount<T> couponDiscount;

    public Context(ICouponDiscount<T> couponDiscount) {
        this.couponDiscount = couponDiscount;
    }

    public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {
        return couponDiscount.discountAmount(couponInfo, skuPrice);
    }

}
  • 策略模式的控制类主要是外部可以传递不同的策略实现,在通过统一的方法执行优惠策略计算。
  • 另外这里也可以包装成map结构,让外部只需要对应的泛型类型即可使用相应的服务。

测试类

public class ApiTest {

    private Logger logger = LoggerFactory.getLogger(ApiTest.class);

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

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


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

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

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

总结

通过策略设计模式的使用可以把我们方法中的 if 语句优化掉,大量的 if 语句使用会让代码难以扩展,也不好维护,同时在后期遇到各种问题也很难维护。在使用这样的设计模式后可以很好的满足隔离性与和扩展性,对于不断新增的需求也非常方便承接。

了解策略模式的优点和缺点,合理的使用策略模式,会让你的代码更加的整洁优雅。

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

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

相关文章

基于BP神经网络对MNIST数据集检测识别(Pytorch,Tensorflow版本)

基于BP神经网络对MNIST数据集检测识别 1&#xff0e;作者介绍2&#xff0e;基于BP神经网络对MNIST数据集检测识别2.1 BP神经网络介绍2.2 神经元模型2.3 激活函数2.4 BP神经网络基础架构2.5 BP神经网络正向传播反向传播 3&#xff0e;基于BP神经网络对MNIST数据集检测识别实验3.…

Playwright系列:第14章 Playwright性能测试实战

下方查看历史精选文章 重磅发布 - 自动化框架基础指南pdfv1.1大数据测试过程、策略及挑战 测试框架原理&#xff0c;构建成功的基石 在自动化测试工作之前&#xff0c;你应该知道的10条建议 在自动化测试中&#xff0c;重要的不是工具 功能测试可以验证应用程序的功能是否正常,…

Cocos Store打通企业提现,诚邀更多团队和公司入驻!

2023年5月29日 Cocos Store 终于上线&#xff0c;令众多开发者期盼已久的功能——企业对公提现。 在此&#xff0c;真诚地对大家说一声&#xff1a;抱歉&#xff0c;我们来晚了&#xff01; 但请一定相信 Cocos 引擎、Cocos Store 一直在努力并从未间断为开发者们创造价值&…

chatgpt赋能python:小黑框:Python程序员必备利器

小黑框&#xff1a;Python程序员必备利器 如果您是一名Python程序员&#xff0c;小黑框&#xff08;Terminal&#xff09;一定不陌生。小黑框是一种基于文本的用户界面&#xff0c;通常用于执行命令行任务&#xff0c;编写或调试代码等。Python程序员可以通过小黑框完成许多任…

【JUC基础】16. Fork Join

1、前言 “分而治之”一直是一个非常有效的处理大量数据的方法。著名的MapReduce也是采取了分而治之的思想。。简单地说&#xff0c;就是如果你要处理 1000 个数据&#xff0c;但是你并不具备处理 1000个数据的能力&#xff0c;那么你可以只处理其中的 10 个&#xff0c;然后分…

使用JSAPl来做一个倒计时的效果

今天的小案例需要做一个倒计时的效果 我们的时分秒需要一直进行倒计时&#xff0c;然后我们的页面颜色需要根据定时器的操作来进行更换&#xff0c;首先我们还是可以来分析一下我们的HTML步骤 <div class"countdown"><p class"next">今天是22…

HCIE-Cloud Computing LAB备考第二步:实战解题–第五题:论述二--跨数据中心部署问答--缩写法

跨数据中心部署 通常部署在同城或相近城市存在的两个数据中心&#xff0c;其物理距离在300km以内&#xff0c;两个数据中心均处于运行状态&#xff0c;可同时承担相同业务&#xff0c;提高数据中心的整体服务能力和系统资源利用率&#xff0c;当单数据中心故障时&#xff0c;业…

学会使用perf性能分析工具(含移植到arm-linux开发板)

文章目录 一、在ubuntu中使用apt包下载Perf二、使用源码安装Perf&#xff0c;并移植到arm-linux环境下三、使用perf四、Perf的功能介绍 系统&#xff1a;Ubuntu18.04系统 内核版本&#xff1a;5.4.0-150-generic&#xff08;通过uname -r查看&#xff09; 一、在ubuntu中使用ap…

针对大屏设备优化 Android 应用的方式及相关注意事项

作者 / Android 团队 近年来&#xff0c;包括大型可折叠设备、平板电脑以及 Chromebook 等大屏 Android 设备的数量与日俱增。确保应用可以在大屏设备上为用户提供无缝体验比以往任何时候都更加重要。例如&#xff0c;用户希望应用能够更充分利用这些设备的更大屏幕空间。我们发…

数据库信息速递 MONGODB CTO 看数据库发展趋势 与 不使用MONGODB你就要交“创新税”...

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群 3群&#xff…

为了女神,我拼了!

大家注意&#xff1a;因为微信最近又改了推送机制&#xff0c;经常有小伙伴说错过了之前被删的文章&#xff0c;比如前阵子冒着风险写的爬虫&#xff0c;再比如一些限时福利&#xff0c;错过了就是错过了。 所以建议大家加个星标&#xff0c;就能第一时间收到推送。&#x1f44…

第二十八章 开发Productions - ObjectScript Productions - 定义业务操作

文章目录 第二十八章 开发Productions - ObjectScript Productions - 定义业务操作介绍关键原则定义业务操作类 第二十八章 开发Productions - ObjectScript Productions - 定义业务操作 本页介绍如何定义业务操作类。 提示&#xff1a; IRIS 提供使用特定出站适配器的专用业…

解读大模型的微调

在快速发展的人工智能领域中&#xff0c;有效地利用大型语言模型&#xff08;LLM&#xff09;变得越来越重要。然而&#xff0c;有许多不同的方式可以使用大型语言模型&#xff0c;这可能会让我们感到困惑。实际上&#xff0c;可以使用预训练的大型语言模型进行新任务的上下文学…

【移动架构】Flutter和React Native:最后的PK

首先&#xff0c;有点离题。做出决定的最简单方法是回顾历史。让我们沿着怀旧之路走一趟。早在2000年初&#xff0c;JAVA就有两个UI框架。一个是AWT&#xff0c;它是一种为多个操作系统构建UI的方法&#xff0c;同时仍然保持操作系统的外观。 每个操作系统都有自己的组件&#…

驱动开发--创建设备文件--控制LED灯

目录 1、手动创建设备文件 2、应用程序如何将数据传递给驱动 3、控制LED灯&#xff1a; 4、应用层控制灯 5、自动创建设备节点 1、手动创建设备文件 cat /proc/devices 查看主设备号 sudo mknod hello(路径&#xff1a;任意的) c/b&#xff08;C代表字符设备 b代表块设备&a…

华为无线AC双机热备三层组网配置案例

核心交换机: dis current-configuration sysname hx undo info-center enable vlan batch 10 66 88 99 to 100 ip pool vlan10 gateway-list 192.168.10.254 network 192.168.10.0 mask 255.255.255.0 dns-list 8.8.8.8 ip pool vlan100 gateway-list 172.16.100.254 network …

Qt在MySQL中存储音频文件

一、在存储音频视频等大文件时需要以二进制文件进行存储&#xff0c;首先需要了解mysql存储二进制文件的字段类型以及大小&#xff1a; 需要创建数据库中的图片类型为&#xff1a;二进制mediumblob类型&#xff0c;&#xff08; TinyBlob 最大 255 Blob 最大 65K MediumBlob …

苹果发布会,卧槽,卧槽,卧槽

今天跟二哥在群里聊到苹果的发布会&#xff0c;二哥完整的看了发布会&#xff0c;我随口问一句二哥看完后什么感受。 二哥说「苹果的工业设计还是遥遥领先&#xff0c;交互设计也是一流水准&#xff0c;然后价格也是遥遥领先」。 然后&#xff0c;我今天也抽空看了关于苹果新发…

【算法与数据结构】203、LeetCode移除链表元素

文章目录 题目一、解题思路完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 题目 一、解题思路 思路分析&#xff1a;这道题需要注意一个特殊情况&#xff0c;当删除的是头结点时&#xff0c;直接删除就找不到整个链表。因此我…

2023LRC软件、Adobe Lightroom Classic下载、安装教程

最后附下载地址 LRC简介&#xff1a; Adobe Lightroom Classic&#xff08;简称LR&#xff09;是Adobe Creative Cloud大家庭中的一款专业的图片管理和编辑工具&#xff0c;用于专业摄影师、摄影爱好者以及所有不断优化数码影像的人等。其目标是以丰富的功能提供高效、一致的…