设计模式-行为型模式-策略模式

一、什么是策略模式

        策略模式是一种行为设计模式,它允许在运行时选择算法或行为,并将其封装成独立的对象,使得这些算法或行为可以相互替换,而不影响使用它们的客户端。(ChatGPT生成)

        主要组成部分:

        1、策略(Strategy): 定义了一个算法族、行为或方法,并将其封装在一个接口或抽象类中,使得这些算法可以相互替换。

        2、具体策略(Concrete Strategy): 实现了策略接口,提供了具体的算法或行为实现。

        3、上下文(Context): 持有一个策略对象的引用,在运行时可动态切换不同的策略。

        原理:

        1、策略模式允许在运行时动态选择算法或行为,通过将具体的算法或行为封装成策略对象,然后将这些策略对象注入到上下文中。

        2、上下文根据不同的需求或条件选择合适的策略对象,并使用它执行特定的算法或行为。

        优点:

        1、易于扩展和维护: 可以方便地添加新的策略或修改现有策略,不影响原有代码。

        2、避免条件语句: 可以避免大量的条件语句,提高代码可读性和可维护性。

        3、代码复用: 可以通过共享策略对象来提高代码的复用性。

二、场景模拟

        为了应用策略模式,我们模拟一个场景。在一个企业资源管理(ERP)系统中,可能会涉及到成本的计算,其中需要计算的成本例如:钢材、油漆、人工、运输等等,虽然说计算成本都是“单价*数量”,但是有些成本,例如钢材,不仅要计算钢材的成本,而且放置钢材或者其它费用,也需要进行一定的计费;对于人工成本,还需要计算企业担负的五险一金等。

 如上图所示,钢材是由吨数*单价计算的,但是要考虑保管费或其他费用,假设一吨n元,后续还可能考虑其它费用等;人工费用,雇员一人可能工资12000元,公司要负担其12%公积金或者医疗保险等;运输要考虑车辆维护费或者日常损耗。(以上仅是模拟,真实环境可能更加复杂或简单)。

三、业务实现

3.1、使用IF-ELSE方法

public static BigDecimal useIfElse(String name, BigDecimal amount, BigDecimal price){if (name.equals("plate")) {// 单价×数量,再加上每吨200元的其它费用return amount.multiply(price).add(amount.multiply(new BigDecimal("200.00")));} else if (name.equals("paints")) {// 单价×数量,再加上每吨200元的其它费用return amount.multiply(price).add(amount.multiply(new BigDecimal("100.00")));} else if (name.equals("employer")) {// 人员×数量,再加上每吨200元的其它费用BigDecimal sum = amount.multiply(price); // 基本工资sum = sum.add(amount.multiply(price.multiply(new BigDecimal("0.12")))); // 加上额外支付的12%公积金return sum;} else if (name.equals("haulage")) {// 吨数×数量,再加上每吨300元的损耗费用return amount.multiply(price).add(amount.multiply(new BigDecimal("300.00")));} else {log.error("无法匹配");return new BigDecimal(0);}}

我们用IF-ELSE方法,确实是可以实现功能,可以看出来很麻烦,看起来很混乱,没有结构,也没办法实现一些定制功能,后续变动起来也比骄困难,因为有一些东西都是写死的。我们来测试一下:

public static void main(String[] args) {// 钢板单价及数量BigDecimal plateAmount = new BigDecimal(15);BigDecimal platePrice = new BigDecimal("888.88");// 油漆价格及数量BigDecimal paintsAmount = new BigDecimal(20);BigDecimal paintsPrice = new BigDecimal("490.00");// 雇员工资及人数BigDecimal employerAmount = new BigDecimal(10);BigDecimal employerPrice = new BigDecimal("10000.00");// 运输价格及吨数BigDecimal haulageAmount = new BigDecimal(25);BigDecimal haulagePrice = new BigDecimal("3000.00");// 计算各个成本BigDecimal plate = useIfElse("plate", plateAmount, platePrice);BigDecimal paints = useIfElse("paints", paintsAmount, paintsPrice);BigDecimal employer = useIfElse("employer", employerAmount, employerPrice);BigDecimal haulage = useIfElse("haulage", haulageAmount, haulagePrice);BigDecimal sum = plate.add(paints).add(employer).add(haulage);log.info("钢材:{}元,油漆{}元,雇员:{}元,运输:{}元,总计:{}元", plate, paints, employer, haulage, sum);}

可以看到确实正确的计算出了结果,但是这种方式不具有拓展性,随着程序功能的增加,会越来越混乱。那有些同学可能会说,为什么不用Switch-Case方法?那就来实践一下。

3.2、使用Switch-Case方法

public static BigDecimal useSwitch(String name, BigDecimal amount, BigDecimal price){switch (name) {case "plate":// 单价×数量,再加上每吨200元的其它费用return amount.multiply(price).add(amount.multiply(new BigDecimal("200.00")));case "paints":// 单价×数量,再加上每吨200元的其它费用return amount.multiply(price).add(amount.multiply(new BigDecimal("100.00")));case "employer":// 人员×数量,再加上每吨200元的其它费用BigDecimal sum = amount.multiply(price); // 基本工资sum = sum.add(amount.multiply(price.multiply(new BigDecimal("0.12")))); // 加上额外支付的12%公积金return sum;case "haulage":// 吨数×数量,再加上每吨300元的损耗费用return amount.multiply(price).add(amount.multiply(new BigDecimal("300.00")));default:log.error("无法匹配");return new BigDecimal(0);}}

使用Switch-Case方法后,确实代码变得比较清晰了,但是一些东西仍然是写死的,不具备很好的扩展性。

3.3、使用策略模式重构代码

        其中ICosts是一个接口,里面只有一个方法,就是成本的计算。他的实现类包含钢材、油漆、人工等等,可以有很多。Context是策略控制类,外部可以传递不同策略执行计算成本方法。

 3.3.1、编写成本计算接口

public interface ICosts<T, K, V> {/*** 成本计算* @param amount 数量* @param price 单价* @param other 其它信息* @return 成本金额*/BigDecimal calculateCosts(T amount, BigDecimal price, Map<K, V> other);
}

 解释一下amount为什么要用T表示,因为这里数量不仅仅是BigDecimal形式的,可能会涉及到人、车辆等等,这种是没有小数点的……;关于other就是一些附加信息,例如钢材的除锈费用、维护费用,人员的五险一金比例等等。

3.3.2、具体成本实现类

钢材

public class GCCosts implements ICosts<BigDecimal, String, BigDecimal> {/** 钢材成本*   1、基本成本:吨数 * 价格;*   2、存放成本:吨数 * 存放消费;*   3、防锈成本:吨数 * 防锈成本;* */@Overridepublic BigDecimal calculateCosts(BigDecimal amount, BigDecimal price, Map<String, BigDecimal> other) {BigDecimal sum = amount.multiply(price);// 计算存放成本if (other.containsKey("CF")) {sum = sum.add(amount.multiply(other.get("CF")));}// 计算防锈成本if (other.containsKey("CX")) {sum = sum.add(amount.multiply(other.get("CX")));}return sum;}
}

油漆

public class YQCosts implements ICosts<BigDecimal, String, BigDecimal> {/** 油漆成本*   1、基础成本:单价 * 数量;*   2、存放成本: 数量 * 存放费用;* */@Overridepublic BigDecimal calculateCosts(BigDecimal amount, BigDecimal price, Map<String, BigDecimal> other) {BigDecimal sum = amount.multiply(price);if (other.containsKey("CF")) {sum = sum.add(amount.multiply(other.get("CF")));}return sum;}
}

人工

public class RGCosts implements ICosts<Long, String, BigDecimal> {/** 人工成本:*   1、基础成本:人数 * 工资;*   2、公积金成本: 人数 * (工资 * 公积金比例);*   3、五险成本:人数 * (工资 * 企业分摊比例);* */@Overridepublic BigDecimal calculateCosts(Long amount, BigDecimal price, Map<String, BigDecimal> other) {// 先计算每个人成本BigDecimal personCosts = price;// 加公积金if (other.containsKey("GJJ")) {personCosts = personCosts.add(price.multiply(other.get("GJJ")));}// 加五险if (other.containsKey("BX")) {personCosts = personCosts.add(price.multiply(other.get("BX")));}return personCosts.multiply(BigDecimal.valueOf(amount)); // 计算总成本}
}

运输

public class YSCosts implements ICosts<BigDecimal, String, BigDecimal> {/** 运输成本:*   1、基础成本:运输数量 * 运输单价;*   2、损耗费用:例如换机油,养护等费用,固定金额(模拟);* */@Overridepublic BigDecimal calculateCosts(BigDecimal amount, BigDecimal price, Map<String, BigDecimal> other) {BigDecimal sum = amount.multiply(price);// 加上固定的养护费用if (other.containsKey("YH")) {sum = sum.add(other.get("YH"));}return sum;}
}

3.3.3、成本策略控制类

public class CostsContext<T, K, V>{private ICosts<T, K, V> iCosts;public CostsContext(ICosts<T, K, V> iCosts) {this.iCosts = iCosts;}public BigDecimal calculateCosts(T amount, BigDecimal price, Map<K, V> other) {return iCosts.calculateCosts(amount, price, other);}
}

此类充当一个调度作用,不同的成本计算就选择不同的实现类,然后执行calculateCosts()方法。

3.3.4、测试

public static void main(String[] args) {// 钢板单价及数量BigDecimal plateAmount = new BigDecimal(15);BigDecimal platePrice = new BigDecimal("888.88");Map<String, BigDecimal> plateMap = new HashMap<String, BigDecimal>(){{put("CF", new BigDecimal("300"));put("CX", new BigDecimal("200"));}};// 油漆价格及数量BigDecimal paintsAmount = new BigDecimal(20);BigDecimal paintsPrice = new BigDecimal("490.00");Map<String, BigDecimal> paintsMap = new HashMap<String, BigDecimal>(){{put("CF", new BigDecimal("300"));}};// 雇员工资及人数// BigDecimal employerAmount = new BigDecimal(10);Long employerAmount = 10L;BigDecimal employerPrice = new BigDecimal("10000.00");Map<String, BigDecimal> employerMap = new HashMap<String, BigDecimal>(){{put("GJJ", new BigDecimal("0.12"));put("BX", new BigDecimal("0.06"));}};// 运输价格及吨数BigDecimal haulageAmount = new BigDecimal(25);BigDecimal haulagePrice = new BigDecimal("3000.00");Map<String, BigDecimal> haulageMap = new HashMap<String, BigDecimal>(){{put("YH", new BigDecimal("1500"));}};// 使用策略模式计算// 钢材CostsContext<BigDecimal, String, BigDecimal> plateCost = new CostsContext<>(new GCCosts());BigDecimal plate = plateCost.calculateCosts(plateAmount, platePrice, plateMap);// 油漆CostsContext<BigDecimal, String, BigDecimal> paintsCost = new CostsContext<>(new YQCosts());BigDecimal paints = paintsCost.calculateCosts(paintsAmount, paintsPrice, paintsMap);// 人工CostsContext<Long, String, BigDecimal> employerCost = new CostsContext<>(new RGCosts());BigDecimal employer = employerCost.calculateCosts(employerAmount, employerPrice, employerMap);// 运输CostsContext<BigDecimal, String, BigDecimal> haulageCost = new CostsContext<>(new YQCosts());BigDecimal haulage = haulageCost.calculateCosts(haulageAmount, haulagePrice, haulageMap);BigDecimal sum = plate.add(paints).add(employer).add(haulage);log.info("钢材:{}元,油漆{}元,雇员:{}元,运输:{}元,总计:{}元", plate, paints, employer, haulage, sum);}

上面是一些各种成本的数量和单价信息,同时使用Map存放了一些特有的成本信息,然后下面我们使用成本策略控制类CostsContext装载不同的实现类来进行成本的计算。

可以看到成功的计算出了结果。我们不仅实现了功能,也为后续的扩展打下了良好的基础,例如后续加新的成本类型进行计算,我们只需要继承ICosts实现不同的方法即可,如果某一个类型的计算加了一些条件,这样我们直接在Map里面添加条件,并在相应的实体类进行处理即可,这样让我们的代码结构变得清晰。

四、小结 

        if-else是我们初学代码时就接触到的代码,伴随我们走过了很多年,但是我们的思维一定要有提高,不要一有需求,就想到if-else,确实,if-else可以快速地实现功能,我们不可否认,但是随着项目复杂度的提高,if-else已经无法满足我们的需求,它会让我们的代码变得臃肿、复杂、结构混乱,所以我们就想到要用其它方法来替代if-else,让我们整个代码结构变得清晰。

        上面我们介绍了使用策略模式来代替一堆的if-else,策略模式的优点远不止如此:

        1、具有强的扩展性,例如我们扩充新的成本计算,只需要新建类并实现接口即可;

        2、易维护和理解,如何我么要看或者修改钢材的成本计算,我们不用到一堆if-else里面去找具体的代码块,我们只需要进去相应的实现类去看就可以,代码清晰明了,没有其他代码的干扰(体现出隔离性),我们维护起来也得心应手;

        3、使用策略模式可以让我们代码变得优美,思维能力和架构水平得到提升。

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

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

相关文章

基于django的在线教育系统

基于python的在线教育系统 摘要 基于Django的在线教育系统是一种利用Django框架开发的现代化教育平台。该系统旨在提供高效、灵活、易用的在线学习体验&#xff0c;满足学生、教师和管理员的需求。系统包括学生管理、课程管理、教师管理、视频课程、在线测验等核心功能。系统采…

docker 部署Redis集群(三主三从,以及扩容、缩容)

1&#xff1a;创建6个redis容器 docker run -d --name redis01 --net host --privilegedtrue -v /opt/redis/redis01:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381 docker run -d --name redis02 --net host --privilegedtrue -v /opt/redis/redis0…

在线 sha1 加密

ttmd5 http://www.ttmd5.com/hash.php?type5 qqxiuzi https://www.qqxiuzi.cn/bianma/sha-1.htm jb51 http://tools.jb51.net/password/sha_encode

Kubernetes实战(五)-pod之间网络请求实战

1 同namespace内pod网络请求 1.1 创建namespace ygq $ kubectl create namespace ygq namespace/ygq created 1.2 创建svc和deployment 在naemspace ygq下创建两个应用&#xff1a;nginx和nginx-test。 1.2.1 部署应用nginx $ cat nginx-svc.yaml apiVersion: v1 kind: …

立哥国家示范项目-5G智慧文旅

项目总体技术方案&#xff1a; 1、旅游5G专网建设&#xff1a;是基于公网授权频谱&#xff0c;采用专线形式&#xff0c;使用MEC服务器为用户提供服务,边缘计算使用Edge VLAVR支持多类型应用&#xff0c;并通过编排实现边缘业务的构建。解决了信号密度覆盖小、强度弱的问题。 …

代码随想录二刷 | 数组 | 总结篇

代码随想录二刷 &#xff5c; 数组 &#xff5c; 总结篇 基础知识二分查找移除元素有序数组的平方长度最小的数组最小覆盖子串螺旋数组 基础知识 定义&#xff1a;数组是存放在连续内存空间上的相同类型数据的集合 特点&#xff1a; 数组下标从 0 开始数组内存空间的地址是连…

Golang Context 的并发安全性探究

在 Golang 中&#xff0c;Context 是一个用于管理 goroutine 生命周期、传递请求和控制信息的重要机制。然而&#xff0c;当多个 goroutine 同时使用 Context 时&#xff0c;很容易出现并发安全性问题。本文将探讨如何正确使用 Context 并保证其在并发环境下的安全性。 1. Con…

23111707[含文档+PPT+源码等]计算机毕业设计基于javawebmysql的旅游网址前后台-全新项目

文章目录 **软件开发环境及开发工具&#xff1a;****功能介绍&#xff1a;****论文截图&#xff1a;****实现&#xff1a;****代码:** 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 软件开发环境及开发工具&#xff1a; 前端使用技术&a…

mock测试数据

1.下载一个jar 架包 地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1G5rVF5LlIYpyU-_KHsGjOA?pwdab12 提取码&#xff1a;ab12 2.配置当前电脑java环境变量 3.在同一文件目录下创建json 数据4.在终端切换到当前目录下启动服务&#xff0c; java -jar ./moco-r…

力扣:171. Excel 表列序号(Python3)

题目&#xff1a; 给你一个字符串 columnTitle &#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如&#xff1a; A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; …

使用百度翻译API或腾讯翻译API做一个小翻译工具

前言 书到用时方恨少&#xff0c;只能临时抱佛脚。英文pdf看不懂&#xff0c;压根看不懂。正好有百度翻译API和腾讯翻译API&#xff0c;就利用两个API自己写一个简单的翻译工具&#xff0c;充分利用资源&#xff0c;用的也放心。 前期准备 关键肯定是两大厂的翻译API&#x…

IDEA 集成 Docker 插件一键部署 SpringBoot 应用

目录 前言IDEA 安装 Docker 插件配置 Docker 远程服务器编写 DockerFileSpringBoot 部署配置SpringBoot 项目部署结语 前言 随着容器化技术的崛起&#xff0c;Docker成为了现代软件开发的关键工具。在Java开发中&#xff0c;Spring Boot是一款备受青睐的框架&#xff0c;然而&…

kubenetes-服务发现和负载均衡

一、服务发布 kubenetes把服务发布至集群内部或者外部&#xff0c;服务的三种不同类型&#xff1a; ClusterlPNodePortLoadBalancer ClusterIP是发布至集群内部的一个虚拟IP,通过负载均衡技术转发到不同的pod中。 NodePort解决的是集群外部访问的问题&#xff0c;用户可能不…

debian 修改镜像源为阿里云【详细步骤】

文章目录 修改步骤第 1 步:安装 vim 软件第 2 步:备份源第 3 步:修改为阿里云镜像参考👉 背景:在 Docker 中安装了 jenkins 容器。查看系统,发现是 debian 11(bullseye)。 👉 目标:修改 debian bullseye 的镜像为阿里云镜像,加速软件安装。 修改步骤 第 1 步:…

限制Domain Admin登录非域控服务器和用户计算机

限制Domain Admin管理员使用敏感管理员帐户(域或林中管理员组、域管理员组和企业管理员组中的成员帐户)登录到信任度较低的服务器和用户端计算机。 此限制可防止管理员通过登录到信任度较低的计算机来无意中增加凭据被盗的风险。 建议采用的策略 建议使用以下策略限制对信任度…

SPASS-偏相关分析

基本概念 偏相关分析的任务就是在研究两个变量之间的线性相关关系时控制可能对其产生影响的变量,这种相关系数称为偏相关系数。偏相关系数的数值和简单相关系数的数值常常是不同的,在计算简单相关系数时,所有其他自变量不予考虑。 统计原理 控制一个变量和控制两个变量的偏…

Python winreg将cmd/PowerShell(管理员)添加到右键菜单

效果 1. 脚本 用管理员权限运行&#xff0c;重复执行会起到覆盖效果&#xff08;根据sub_key&#xff09;。 icon自己设置。text可以自定义。sub_key可以改但不推荐&#xff08;避免改成和系统已有项冲突的&#xff09;。command不要改。 from winreg import *registry r&q…

Flutter执行flutter doctor报错HTTP Host Availability

问题描述 [!] HTTP Host Availability✗ HTTP host https://maven.google.com/ is not reachable. Reason: An erroroccurred while checking the HTTP host: Operation timed out解决方案 将文件flutter/packages/flutter_tools/lib/src/http_host_validator.dart中的https:…

在 Qt 框架中,有许多内置的信号可用于不同的类和对象\triggered

在 Qt 框架中&#xff0c;有许多内置的信号可用于不同的类和对象 以下是一些常见的内置信号的示例&#xff1a; clicked()&#xff1a;按钮&#xff08;QPushButton、QToolButton 等&#xff09;被点击时触发的信号。 pressed() 和 released()&#xff1a;按钮被按下和释放时…

ubuntu20.04安装cv2

查看ubuntu的版本 cat /etc/lsb-release DISTRIB_IDUbuntu DISTRIB_RELEASE20.04 DISTRIB_CODENAMEfocal DISTRIB_DESCRIPTION"Ubuntu 20.04.3 LTS"更改镜像源 cp /etc/apt/sources.list /etc/apt/sources.list.bak cat > /etc/apt/sources.listdeb http://mirr…