3分钟看懂设计模式01:策略模式
一、什么是策略模式
定义一些列算法类,将每一个算法封装起来,并让它们可以互相替换。
策略模式让算法独立于使用它的客户而变化,是一种对象行为型模式。
以上是策略模式的一般定义,属于是课本内容。
在没有真正理解策略模式之前并不需要对此定义下过多功夫,读一遍直接进入下一章节。
二、为什么要用策略模式
我们应该知道,所谓的设计模式实际上是一种经过检验的、科学高效的、针对某种场景的最佳编程设计实践。
所以要理解某一种设计模式,就必须知道我们什么时候可以用,用之前和用之后到底有什么区别。
练习:
假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。
一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。
比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。
根据描述,折扣是根据以下的几个算法中的一个进行的:
算法一:对初级会员没有折扣。
算法二:对中级会员提供10%的促销折扣。
算法三:对高级会员提供20%的促销折扣。
给出一本图书,如300元,若是高级会员,则输出价格为240元。

针对以上场景,大多数的我们写的代码就是使用的if…else…。
我们先提前揭晓,这种场景下就是我们使用策略模式的最佳时机。
那在我们尝试使用策略模式改进代码之前,我们必须要问:
if…else到底有什么问题?
传统实现方式
public Double calculationPrice(String type, Double originalPrice, int n) {//中级会员计费if (type.equals("intermediateMember")) {return originalPrice * n - originalPrice * 0.1;}//高级会员计费if (type.equals("advancePrimaryMember")) {return originalPrice * n - originalPrice * 0.2;}//普通会员计费return originalPrice;
}
这种编码方式到底差在哪?
大佬告诉我们说:维护性差。
什么叫维护性差?
就是下次你想加个超级黄金vip会员,以及各种后续会员种类,你就要不断往里加if…else…,这就违反了开闭原则。
这里又有另外两个问题:
什么是开闭原则?我凭啥要遵守开闭原则?
什么是开闭原则?
开闭原则比较好记忆,顾名思义:
对扩展开放,对修改关闭。
大意就是你想改东西,不要改原代码,而是进行扩展代码。
为什么要遵守开闭原则(以及各种乱七八糟的原则)?
简单直接一点就是,这些原则都是巨佬们总结出来的,你如果不懂,你就直接选择相信就好了。
解释一下就是:
系统随着开发的不断进展,需求不断增多,代码越来越长,如果没有合理框架的制约那就只能沦为一个扩展难、维护难的屎山。
所以我们遵守开闭原则就是说需要一个科学合理的框架规范我们的系统熵增,在不修改原代码的基础上让系统拥有灵活性和稳定性。
一句话,上面的代码直接修改原代码,时间久了系统只会沦为屎山。
那怎么使用策略模式改造,而遵守开闭原则呢?
策略模式如何实现
Strategy(抽象策略类)
它为所支持的算法声明抽象方法,是所有策略类的父类。它可以使抽象类或者具体类,也可以是接口。
public interface MemberStrategy {// 一个计算价格的抽象方法//price商品的价格 n商品的个数public double calcPrice(double price, int n);
}
ConcreteStrategy(具体策略类)
它实现了上面抽象策略类的抽象方法。
在实际运行中,这个具体的策略类将会代替在**环境类(Context)**中定义的抽象策略类对象最终执行不同的实现逻辑。
可以看到下面的代码中,三种不同的策略类实现了同一个抽象策略类,每种策略对应一种实现,分别应对一个业务处理方式。
// 普通会员——不打折
public class PrimaryMemberStrategy implements MemberStrategy { // 实现策略@Overridepublic double calcPrice(double price, int n) {return price * n;}
}// 中级会员 打百分之10的折扣
public class IntermediateMemberStrategy implements MemberStrategy{@Overridepublic double calcPrice(double price, int n) {double money = (price * n) - price * n * 0.1;return money;}
}// 高级会员类 20%折扣
public class AdvanceMemberStrategy implements MemberStrategy{@Overridepublic double calcPrice(double price, int n) {double money = price * n - price * n * 0.2;return money;}
}
Context(环境类)
这个对我来说一开始很难理解。
主要是不能理解 Context 这个词在这里的意思,再加上网上一大堆直接翻译为“上下文”的文章博客,我直接吐了:
我不理解Context的意思,难道就能理解“上下文”的意思?
还有类似的:事务又是什么东西?
所以我直接不管这个Context是什么东西,直接看代码。
首先他是一个类,我们看这个类里有什么。
一个成员变量memberStrategy
一个构造方法
一个计算价格的方法,内容返回memberStrategy的calcPrice方法
往下看。
/*** 负责和具体的策略类交互* 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化。*/// 上下文类/环境类
public class MemberContext {// 用户折扣策略接口private MemberStrategy memberStrategy;// 注入构造方法public MemberContext(MemberStrategy memberStrategy) {this.memberStrategy = memberStrategy;}// 计算价格public double qoutePrice(double goodsPrice, int n){// 通过接口变量调用对应的具体策略return memberStrategy.calcPrice(goodsPrice, n);}}
接下来看测试类中 Context 类的使用是什么样子的。
// 测试类public class Application {public static void main(String[] args) {// 具体行为策略MemberStrategy primaryMemberStrategy = new PrimaryMemberStrategy(); // 接口回调(向上转型)MemberStrategy intermediateMemberStrategy = new IntermediateMemberStrategy();MemberStrategy advanceMemberStrategy = new AdvanceMemberStrategy();// 用户选择不同策略MemberContext primaryContext = new MemberContext(primaryMemberStrategy);MemberContext intermediateContext = new MemberContext(intermediateMemberStrategy);MemberContext advanceContext = new MemberContext(advanceMemberStrategy);//计算一本300块钱的书System.out.println("普通会员的价格:"+ primaryContext.qoutePrice(300,1));// 普通会员:300System.out.println("中级会员的价格:"+ intermediateContext.qoutePrice(300,1));// 中级会员 270System.out.println("高级会员的价格:"+ advanceContext.qoutePrice(300,1));// 高级会员240}}
发现了什么?
Context都是被new出来的,new的时候传入的Strategy实现类全部不一样,你传的不一样,将来context.calcPrice()执行的逻辑就不一样。
懂了没有?
什么是上下文?
什么是Context?
就是随机应变,像变色龙一样随着不同的环境变化而自由变化。
开发者根据“上下文”不同的业务需求往Context里面放置不同的Strategy。
这就是Context上下文的意思。
这里的Strategy可以你自己new,你也可以把它放在配置类里面配置,然后在代码中读取,这样更加灵活方便。
三、使用策略模式的场景总结
那我们知道了策略模式怎么实现,也就是已经有了一把锤子在手上了,那什么时候用这把锤子呢?
1. 系统中需要动态地在几种算法中选择一种。
2. 一个对象有很多的行为,如果不用策略模式就只能用一大堆的if…else…来实现。
3. 不希望客户端知道复杂的、与算法相关的数据结构。在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。
然后我们用一些实际的例子来理解策略模式的大概的使用场景:
1. 支付方式选择: 假设平台支持多种支付方式,比如微信、支付宝、银行卡等。
2. 数据渲染方式: 如果你有一个应用程序,它可以以多种格式输出数据,比如XML、JSON或CSV。
3. 导航策略: 导航应用多种路径计算方法,如最快路线、最短路线、避开收费路线等。
4. 压缩数据: 根据不同的情况(比如压缩率、速度等)使用不同的压缩算法(如ZIP、RAR、7z等)。
四、策略模式有什么好处
1. 完美支持了开闭原则。
2. 通过抽象算法和继承实现,避免了大量重复代码。
3. 避免了多重选择语句(硬编码,不易维护)。
往期推荐:
● 师爷,翻译翻译什么叫AOP
● 翻译,师爷师爷什么叫事务
● 纪念JDBC
● SpringBoot实现动态数据源配置
● 聚簇索引、回表与覆盖索引
● Java锁到底是个什么东西

相关文章:
3分钟看懂设计模式01:策略模式
一、什么是策略模式 定义一些列算法类,将每一个算法封装起来,并让它们可以互相替换。 策略模式让算法独立于使用它的客户而变化,是一种对象行为型模式。 以上是策略模式的一般定义,属于是课本内容。 在没有真正理解策略模式之…...
数据结构与算法:算法详解
1. 引言 1.1 算法在计算机科学中的地位和重要性 算法是计算机科学的基石,它指导着计算机在解决各种问题时的行为。一个好的算法可以使得问题的解决更加高效、精确和可靠,因此在计算机科学中具有至关重要的地位。 1.2 学习算法的意义和目标 学习算法不…...
AOSP10 替换系统launcher
本文实现将原生的launcher 移除,替换成我们自己写的launcher。 分以下几个步骤: 一、新建一个自己的launcher项目。 1.直接使用android studio 新建一个项目。 2.修改AndroidManifest.xml <applicationandroid:persistent"true"androi…...
视频互动游戏如何暴打海王和舔狗
前言 前2篇文章回答了游戏的可取之处以及不可复制的地方还有对于这一类的情景互动游戏在2024年的发展预言。第三篇主要是回答在一篇中一个留言的读者问的问题“如何暴打海王和舔狗”,求同存异,希望能够跟更多的读者交流与互相学习。 海王和舔狗的特征 …...
大学生多媒体课程学习网站thinkphp+vue
开发语言:php 后端框架:Thinkphp 前端框架:vue.js 服务器:apache 数据库:mysql 运行环境:phpstudy/wamp/xammp等开发背景 (一) 研究课程的提出 (二)学习网站的分类与界定…...
信息系统项目管理师论文分享(质量管理)
水一篇文章。我发现身边考高项的朋友很多都是论文没过,我想着那就把我的论文分享出来,希望能有帮助。 质量管理 摘要 2020年5月,我作为项目经理参加了“某市某医联体的互联网诊疗(互联网医院和远程医疗)平台”的建设…...
Redis实现滑动窗口限流
常见限流算法 固定窗口算法 在固定的时间窗口下进行计数,达到阈值就拒绝请求。固定窗口如果在窗口开始就打满阈值,窗口后半部分进入的请求都会拒绝。 滑动窗口算法 在固定窗口的基础上,窗口会随着时间向前推移,可以在时间内平滑控…...
SQL Server查询计划(Query Plan)——XML查询计划
6.4.3. XML查询计划 SQL Server中,除了通过GUI工具和相关命令获取图形及文本查询计划外,我们还可以通过相关命令获取XML格式的查询计划,这里惯称其为XML查询计划。 SQL Server 2005版本引入了XML查询计划的新特性,其充分吸收了图形及文本查询计划的优势所在,…...
【day02】每天三道 java后端面试题:Java、C++和Go的区别 | Redis的特点和应用场景 | 计算机网络七层模型
文章目录 1. Java、C和 Go 语言的区别,各自的优缺点?2. 什么是Redis?Redis 有哪些特点? Redis有哪些常见的应用场景?3. 简述计算机网络七层模型和各自的作用? 1. Java、C和 Go 语言的区别,各自的…...
【Flink状态管理(八)】Checkpoint:CheckpointBarrier对齐后Checkpoint的完成、通知与对学习状态管理源码的思考
文章目录 一. 调用StreamTask执行Checkpoint操作1. 执行Checkpoint总体代码流程1.1. StreamTask.checkpointState()1.2. executeCheckpointing1.3. 将算子中的状态快照操作封装在OperatorSnapshotFutures中1.4. 算子状态进行快照1.5. 状态数据快照持久化 二. CheckpointCoordin…...
防御保护第八、九、十、十一天笔记
一、内容安全 1、DFI和DPI技术 --- 深度检测技术 DPI是一种基于应用层的流量检测和控制技术,它会对流量进行拆包,分析包头和应用层的内容,从而识别应用程序和应用程序的内容。这种技术增加了对应用层的分析,识别各种应用…...
【TypeScript基础知识点】的讲解
TypeScript基础知识点 TypeScript基础知识点 TypeScript基础知识点 TypeScript 是一种由 Microsoft 开发和维护的开源编程语言,它是 JavaScript 的一个超集,添加了可选的静态类型和基于类的面向对象编程,以下是一些 TypeScript 的基础知识点…...
牛客周赛 Round 34 解题报告 | 珂学家 | 构造思维 + 置换环
前言 整体评价 好绝望的牛客周赛,彻底暴露了CF菜菜的本质,F题没思路,G题用置换环骗了50%, 这大概是唯一的亮点了。 A. 小红的字符串生成 思路: 枚举 a,b两字符在相等情况下比较特殊 a, b input().split() if a b:print (2)print (a)pri…...
LeetCode13 罗马数字转整数
题目 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&…...
【Hudi】Upsert原理
17张图带你彻底理解Hudi Upsert原理 1.开始提交:判断上次任务是否失败,如果失败会触发回滚操作。然后会根据当前时间生成一个事务开始的请求标识元数据。2.构造HoodieRecord Rdd对象:Hudi 会根据元数据信息构造HoodieRecord Rdd 对象…...
信息系统服务:演绎数字时代的征程
信息系统服务作为数字化时代的基石,已经在人类社会的各个领域发挥着重要作用。本文将从信息系统服务的起源、发展和演化过程,通过生动的例子和准确客观的历史事实,探讨信息系统服务对人类社会的影响与变革。 1. 起源:信息处理的初…...
rust连接postgresql数据库
引入crate: postgres "0.19.7" use postgres::{Client, NoTls, error::Error};fn main() -> Result<(), Error> {let mut client Client::connect("hostlocalhost port5432 dbnamexxxxdb userpostgres passwordxxxxxx", NoTls).un…...
[面试] 什么是死锁? 如何解决死锁?
什么是死锁 死锁,简单来说就是两个或者多个的线程在执行的过程中,争夺同一个共享资源造成的相互等待的现象。如果没有外部干预线程会一直阻塞下去. 导致死锁的原因 互斥条件,共享资源 X 和 Y 只能被一个线程占用; 请求和保持条件…...
网络原理 HTTP _ HTTPS
回顾 我们前面介绍了HTTP协议的请求和响应的基本结构 请求报文是由首行请求头空行正文来组成的 响应报文是由首行形影头空行响应正文组成的 我们也介绍了一定的请求头之中的键值对的属性 Host,Content-type,Content-length,User-agent,Referer,Cookie HTTP协议中的状态码 我们先…...
软件实际应用实例,茶楼收银软件管理系统操作流程,茶室计时计费会员管理系统软件试用版教程
软件实际应用实例,茶楼收银软件管理系统操作流程,茶室计时计费会员管理系统软件试用版教程 一、前言 以下软件以 佳易王茶社计时计费管理系统软件V17.9为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、计时计费&…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
