【设计模式】行为型模式(二):策略模式、命令模式
行为型模式(二):策略模式、命令模式
- 3.策略模式(Strategy)
- 3.1 示例
- 3.1.1 定义策略接口
- 3.1.2 实现具体策略
- 3.1.3 定义上下文类
- 3.1.4 客户端代码
- 3.1.5 输出结果
- 3.2 总结
- 3.2.1 优点
- 3.2.2 缺点
- 4.命令模式(Command)
- 4.1 组成部分
- 4.2 示例
- 4.2.1 命令接口
- 4.2.2 具体命令实现
- 4.2.3 接收者
- 4.2.4 调用者
- 4.2.5 客户端
- 4.2.6 输出结果
- 4.3 总结
- 4.3.1 优点
- 4.3.2 缺点
3.策略模式(Strategy)
策略模式(Strategy)是一种行为设计模式,它使你能在运行时改变对象的行为。下面是策略模式的几个关键点:
- 定义一组算法:策略模式定义了一组可互换的算法(或行为)。这些算法封装在独立的类中,每个类实现一个特定的算法。
- 封装变化:将变化的部分从不变的部分中分离出来,封装成独立的类,这样可以更容易地扩展和维护。
- 运行时选择算法:客户端可以根据需要在运行时选择不同的算法(策略)来执行特定任务。
3.1 示例
假设你正在开发一个电商系统,需要根据不同的促销策略计算订单的最终价格。你可以定义一个 PromotionStrategy 接口,然后为不同的促销策略实现该接口,例如:
FixedDiscountStrategy:固定金额折扣PercentageDiscountStrategy:百分比折扣NoDiscountStrategy:无折扣
在订单类中,你可以使用 PromotionStrategy 接口来计算最终价格,并在运行时根据需要选择不同的策略。
3.1.1 定义策略接口
首先,定义一个策略接口 PromotionStrategy,该接口声明了一个计算折扣价格的方法 calculateDiscount。
public interface PromotionStrategy {double calculateDiscount(double originalPrice);
}
3.1.2 实现具体策略
接下来,实现不同的促销策略,例如 固定金额折扣、百分比折扣 和 无折扣。
public class FixedDiscountStrategy implements PromotionStrategy {private double discountAmount;public FixedDiscountStrategy(double discountAmount) {this.discountAmount = discountAmount;}@Overridepublic double calculateDiscount(double originalPrice) {return originalPrice - discountAmount;}
}public class PercentageDiscountStrategy implements PromotionStrategy {private double discountRate;public PercentageDiscountStrategy(double discountRate) {this.discountRate = discountRate;}@Overridepublic double calculateDiscount(double originalPrice) {return originalPrice * (1 - discountRate);}
}public class NoDiscountStrategy implements PromotionStrategy {@Overridepublic double calculateDiscount(double originalPrice) {return originalPrice;}
}
FixedDiscountStrategy、PercentageDiscountStrategy 和 NoDiscountStrategy 实现了 PromotionStrategy 接口,分别实现了固定金额折扣、百分比折扣和无折扣。
3.1.3 定义上下文类
上下文类 Order 使用 PromotionStrategy 接口来计算最终价格。上下文类可以在运行时选择不同的策略。
public class Order {private double originalPrice;private PromotionStrategy promotionStrategy;public Order(double originalPrice, PromotionStrategy promotionStrategy) {this.originalPrice = originalPrice;this.promotionStrategy = promotionStrategy;}public double getFinalPrice() {return promotionStrategy.calculateDiscount(originalPrice);}public void setPromotionStrategy(PromotionStrategy promotionStrategy) {this.promotionStrategy = promotionStrategy;}
}
3.1.4 客户端代码
客户端代码可以根据需要选择不同的促销策略,并计算订单的最终价格。
public class Client {public static void main(String[] args) {double originalPrice = 100.0;// 固定金额折扣PromotionStrategy fixedDiscountStrategy = new FixedDiscountStrategy(10.0);Order order1 = new Order(originalPrice, fixedDiscountStrategy);System.out.println("Fixed Discount: " + order1.getFinalPrice());// 百分比折扣PromotionStrategy percentageDiscountStrategy = new PercentageDiscountStrategy(0.2);Order order2 = new Order(originalPrice, percentageDiscountStrategy);System.out.println("Percentage Discount: " + order2.getFinalPrice());// 无折扣PromotionStrategy noDiscountStrategy = new NoDiscountStrategy();Order order3 = new Order(originalPrice, noDiscountStrategy);System.out.println("No Discount: " + order3.getFinalPrice());// 动态更改策略order1.setPromotionStrategy(percentageDiscountStrategy);System.out.println("Changed to Percentage Discount: " + order1.getFinalPrice());}
}
客户端代码创建不同的策略对象,并将它们传递给 Order 对象,计算最终价格。还可以在运行时动态更改策略。
3.1.5 输出结果
Fixed Discount: 90.0
Percentage Discount: 80.0
No Discount: 100.0
Changed to Percentage Discount: 80.0
3.2 总结
3.2.1 优点
- 灵活性:可以在运行时动态选择算法。
- 扩展性:新增策略时,只需实现新的策略类,无需修改现有代码。
- 代码复用:不同的上下文可以共享相同的策略。
3.2.2 缺点
- 类的数量增加:每增加一个策略,就需要增加一个类。
- 客户端需要知道所有策略:客户端需要了解所有可用的策略,并选择合适的策略。
4.命令模式(Command)
命令模式(Command)是一种行为设计模式,它将请求封装成一个对象,从而使你能够用不同的请求、队列或者请求日志来参数化其他对象。命令模式也支持可撤销的操作。

4.1 组成部分
- 命令对象:封装了一个请求,包括执行该请求所需的所有信息(例如,接收者、方法、参数等)。
- 接收者:实际执行命令的对象。
- 调用者:请求的发起者,它将命令对象传递给接收者。
- 客户端:创建命令对象并将其设置到调用者中。
4.2 示例
假设我们正在开发一个智能家居系统,用户可以通过 遥控器 控制不同的设备(如 灯、风扇 等)。我们可以使用命令模式来实现这个功能。
4.2.1 命令接口
首先,定义一个命令接口 Command,声明一个执行命令的方法 execute。
public interface Command {void execute();
}
4.2.2 具体命令实现
接下来,实现不同的命令,例如控制灯的开关和控制风扇的开关。
// 开灯
public class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOn();}
}// 关灯
public class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}
}// 开风扇
public class FanOnCommand implements Command {private Fan fan;public FanOnCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.turnOn();}
}// 关闭风扇
public class FanOffCommand implements Command {private Fan fan;public FanOffCommand(Fan fan) {this.fan = fan;}@Overridepublic void execute() {fan.turnOff();}
}
LightOnCommand、LightOffCommand、FanOnCommand 和 FanOffCommand 实现了 Command 接口,分别控制灯和风扇的开关。
4.2.3 接收者
接收者是 实际执行命令的对象,例如灯和风扇。
public class Light {public void turnOn() {System.out.println("Light is on");}public void turnOff() {System.out.println("Light is off");}
}public class Fan {public void turnOn() {System.out.println("Fan is on");}public void turnOff() {System.out.println("Fan is off");}
}
Light 和 Fan 是实际执行命令的对象。
4.2.4 调用者
调用者是 请求的发起者,它将命令对象传递给接收者。
public class RemoteControl {private Command onCommand;private Command offCommand;public RemoteControl(Command onCommand, Command offCommand) {this.onCommand = onCommand;this.offCommand = offCommand;}public void pressOnButton() {onCommand.execute();}public void pressOffButton() {offCommand.execute();}
}
RemoteControl 是请求的发起者,它将命令对象传递给接收者并执行命令。
4.2.5 客户端
客户端代码 创建不同的命令对象,并将它们设置到 RemoteControl 中,通过调用 RemoteControl 的方法来执行命令。
public class CommandPatternDemo {public static void main(String[] args) {Light light = new Light();Fan fan = new Fan();Command lightOn = new LightOnCommand(light);Command lightOff = new LightOffCommand(light);Command fanOn = new FanOnCommand(fan);Command fanOff = new FanOffCommand(fan);RemoteControl remoteControl = new RemoteControl(lightOn, lightOff);remoteControl.pressOnButton(); // 输出: Light is onremoteControl.pressOffButton(); // 输出: Light is offremoteControl = new RemoteControl(fanOn, fanOff);remoteControl.pressOnButton(); // 输出: Fan is onremoteControl.pressOffButton(); // 输出: Fan is off}
}

4.2.6 输出结果
Light is on
Light is off
Fan is on
Fan is off
通过这个例子,你可以看到命令模式如何将请求的发送者和接收者解耦,使代码更加灵活和可扩展。
4.3 总结
4.3.1 优点
- 解耦:命令模式将请求的发送者和接收者解耦,使两者不直接依赖。
- 扩展性:可以很容易地增加新的命令,而不需要修改现有的代码。
- 支持撤销操作:命令对象可以存储执行前的状态,从而支持撤销操作。
4.3.2 缺点
- 类的数量增加:每个命令都需要一个具体的命令类。
- 复杂性增加:引入了多个对象和接口,可能会使系统变得更复杂。
相关文章:
【设计模式】行为型模式(二):策略模式、命令模式
行为型模式(二):策略模式、命令模式 3.策略模式(Strategy)3.1 示例3.1.1 定义策略接口3.1.2 实现具体策略3.1.3 定义上下文类3.1.4 客户端代码3.1.5 输出结果 3.2 总结3.2.1 优点3.2.2 缺点 4.命令模式(Com…...
STM32中断系统
目录 一、中断的基本概念 二、NVIC 1.NVIC的概念 2、NVIC的组成 3、NVIC的应用 4.NVIC的结构 三、外部中断EXTI 1.外部中断的概念 2.EXTI基本结构 四、EXTI外部中断的配置流程 1.开启APB2中的GPIO口/AFIO时钟 2.GPIO配置成输入模式 3.AFIO选择中断引脚 4.EXTI初始…...
window的Anaconda Powershell Prompt 里使用linux 命令
在 Windows 的 Anaconda Powershell Prompt 中使用 Linux 命令,可以通过以下几种方法来实现: 1. 使用 Git Bash 安装 Git for Windows 后,它会包含 Git Bash,允许在 Windows 上使用许多 Linux 命令。 步骤: 安装 Gi…...
Lisp 语言入门教程(一)
Lisp(“LISt Processing”)是一种古老而强大的编程语言,特别适合处理符号数据和列表。Lisp 是一种以括号和递归见长的语言,它启发了许多编程范式。以下是一个基础教程,帮助你快速了解 Lisp 的基本语法和功能。 1. 认识…...
Git - Think in Git
记录一些使用Git时的一些想法 区的概念 当 clone 仓库代码到本地后四个区相同 当编辑代码后,工作区 与其余三个区不同 当使用 add 将修改的代码暂存后,索引区与 工作区 相同 当使用 commit 将修改的代码提交后,仓库区 与 索引区 和 工作区 相…...
jmeter常用配置元件介绍总结之用linux服务器压测
系列文章目录 安装jmeter jmeter常用配置元件介绍总结之用linux服务器压测 1.编写测试脚本2.执行测试脚本 1.编写测试脚本 在linux服务器上进行压测,由于是没有界面的,因此我们可以先在界面上把压测脚本写好: 如图:我这里简单的写…...
VL210-Q4 适用于USB延长线 扩展坞
VL210芯片技术文档 一、概述 VL210是一款由VIA Technologies(威盛电子)生产的第四代先进USB 3.0 Hub控制器。它集成了多种先进技术和功能,适用于各种USB集线器应用,如独立的USB集线器、笔记本/Ultrabook停靠点/port-replicators…...
怎么样绑定域名到AWS(亚马逊云)服务器
1,拿着你买的域名去亚马逊申请一个证书。申请证书分两种,一种是去亚马逊后台填域名手动申请 ,另一种是通过API来申请,类似如下代码: 2、证验证书。有两种方式:一种是通过邮件,另一种去到域名提供…...
Clickhouse集群新建用户、授权以及remote权限问题
新建用户 create user if not exists user on cluster 集群名称 IDENTIFIED WITH plaintext_password BY 密码;给用户授查询、建表、删表的权限 GRANT create table,select,drop table ON 数据库实例.* TO user on cluster 集群名称 ;在其他节点下用户建本地表成功&#…...
OPENCV 检测直线[opencv--3]
opencv中集成了很多好用的函数,比如霍夫变换检测直线的函数,当然,考虑到看我文章的人水平,我这里只讲讲如何使用这个函数,和怎么调节其中的参数 先把运行效果PO出来吧 #include "CV_ERROR.h" #include &q…...
FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.2:avpacket中包含多个 NALU如何解析头部分析
前提: 注意的是:我们这里是从avframe转换成avpacket 后,从avpacket中查看NALU。 在实际开发中,我们有可能是从摄像头中拿到 RGB 或者 PCM,然后将pcm打包成avframe,然后将avframe转换成avpacket࿰…...
【MATLAB】目标检测初探
文章目录 0 前言1 目标检测概述2 算法实践2.1 YOLO v22.2 YOLO v3 3 项目实践3.1 项目背景和数据集3.2 实践结果3.3 算法对比 4 工具箱与数据标注5 总结 0 前言 之前因为项目原因,做了一个基于YOLOv5实现目标检测的程序,是基于Python做的,直接…...
SpringCloud 微服务消息队列灰度方案 (RocketMQ 4.x)
目录 背景遇到的问题 RocketMQ 基础基础消息模型扩展后的消息模型部署模型相关概念点 方案对比影子Topic的方案Tag的方案UserProperty的方案影子Group的方案灰度分区的方案方案对比 灰度分区方案设计适配只有部分灰度的情况所做的功能扩展消费者(无灰度)…...
厘清标准差和标准误:因果推断的统计学基础
标准差,指 一次抽样中 个体取值间的离散程度,反映了 个体取值对样本均值的代表性。 标准误,指 多次抽样中 样本均值间的离散程度,反映了 样本均值对总体均值的代表性。 公众号原文-厘清标准差和标准误:因果推断的统计…...
GESP4级考试语法知识(贪心算法(二))
排队接水2代码: #include<iostream> #include<cstdio> #include<algorithm> using namespace std; struct people {int num;int time; }; people s[1001]; int n,r,a[1001]; double sum,ave; bool cmp(people x,people y) {return x.time<y.t…...
MATLAB 使用教程 —— 命令窗口输入命令,工作区显示变量
命令在命令窗口输入变量在工作区显示 MATLAB 桌面包含的面板如下: 当前文件夹 - 此面板允许访问项目文件夹和文件。命令窗口 - 这是主要区域,用户在命令行中输入命令,命令提示符(>>).工作区 - 工作区显示所有变量,无论是创…...
LeetCode 热题100(八)【二叉树】(3)
目录 8.11二叉树展开为链表(中等) 8.12从前序与中序遍历序列构造二叉树(中等) 8.13路径总和III(中等) 8.14二叉树的最近公共祖先(中等) 8.15二叉树中的最大路径和(困…...
uniapp h5实现录音
使用npm安装 npm install recorder-core引入Recorder库 可以使用import、require、html script等你适合的方式来引入js文件,下面的以import为主要参考,其他引入方式根据文件路径自行调整一下就可以了。 //必须引入的Recorder核心(文件路径是…...
字节跳动Android面试题汇总及参考答案(80+面试题,持续更新)
Android 四大组件是什么? Android 四大组件分别是 Activity、Service、Broadcast Receiver 和 Content Provider。 Activity 是 Android 应用中最基本的组件,用于实现用户界面。它可以包含各种视图控件,如按钮、文本框等。一个 Activity 通常对应一个屏幕的内容。用户可以通…...
【go从零单排】通道select、通道timeout、Non-Blocking Channel Operations非阻塞通道操作
🌈Don’t worry , just coding! 内耗与overthinking只会削弱你的精力,虚度你的光阴,每天迈出一小步,回头时发现已经走了很远。 📗概念 select 语句是 Go 的一种控制结构,用于等待多个通道操作。它类似于 s…...
普冉PY32F003单片机PWM呼吸灯实战:从8ms定时器中断到10KHz波形平滑调节
普冉PY32F003单片机PWM呼吸灯实战:从8ms定时器中断到10KHz波形平滑调节 在嵌入式开发中,PWM(脉冲宽度调制)技术是实现LED亮度渐变、电机调速等功能的基石。普冉PY32F003作为一款高性价比的32位单片机,其定时器模块的灵…...
论基于云原生数据库的企业信息系统架构设计
基于云原生数据库的企业架构随着云原生技术的全面普及,企业信息系统对架构的弹性伸缩、高可靠性、资源高效利用及敏捷迭代能力提出了更高要求。传统数据库存在的存储与计算耦合、扩展能力受限、运维成本高、故障恢复慢等痛点,已难以适配现代化企业的业务…...
GD32F4xx内部FLASH读写避坑指南:从用户手册到代码调试,手把手教你搞定0x08040000地址操作
GD32F4xx内部FLASH操作实战:从手册解读到调试验证的完整指南 第一次接触GD32F4系列MCU的内部FLASH操作时,很多开发者都会遇到各种"坑":为什么擦除后数据变成了0xFF?为什么写入操作会失败?地址0x08040000到底…...
影刀RPA跨境店群自动化实战:Python协同Chromium打破风控“垄断”的高并发调度系统架构
定了。彻底打破传统商业指纹浏览器的生态「垄断」与电商巨头风控体系的「底层封锁」,我们用一套基于 Python 深度协同的分布式微服务调度架构,重塑了跨境千店矩阵的自动化底座。 这几天,科技圈被“DeepSeek V4 首发华为芯片,国产…...
告别龟速下载!保姆级教程:用百度网盘离线下载搞定Android 1.6到16全版本AOSP源码
突破AOSP源码下载瓶颈:高效获取Android全版本开发资源的实战指南 每次打开终端准备下载AOSP源码时,看着缓慢增长的进度条和频繁中断的连接,你是否感到无比沮丧?作为Android开发者,获取完整源码是深入理解系统架构的第一…...
创业团队如何通过taotoken的token plan有效控制ai应用开发成本
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 创业团队如何通过taotoken的token plan有效控制ai应用开发成本 对于资源有限的创业团队和独立开发者而言,在开发AI应用…...
别再傻傻分不清了!用大白话+真实案例讲透OAuth 2.0和OIDC到底差在哪
别再傻傻分不清了!用大白话真实案例讲透OAuth 2.0和OIDC到底差在哪 想象一下这样的场景:你正在开发一个美食分享App,想让用户能直接用微信登录。接入微信开放平台时,技术文档里突然冒出OAuth 2.0和OIDC两个术语,产品经…...
【计算机组成原理】无符号整数乘法原理(基于移位累加,零基础看懂CPU乘法)
前言在数字电路与计算机组成原理中,加法是最基础的运算,而乘法是高频常用运算。很多初学者疑惑:计算机没有专门的乘法口诀,到底怎么实现二进制乘法?而在数字运算中,乘法是比加法更复杂、但底层逻辑完全依托…...
深度解析Linux内核task_struct:从进程管理到性能调优
1. 项目概述:从一行代码到操作系统的心脏 如果你写过C语言程序,一定用过 int main() ,程序启动后,操作系统会为它创建一个“进程”。在Linux的世界里,这个进程在操作系统内核眼中,到底是什么样子的&#…...
从信号处理到AI:卷积的含参积分本质,如何帮你理解PyTorch中的Conv1d层?
从信号处理到AI:卷积的含参积分本质,如何帮你理解PyTorch中的Conv1d层? 在信号处理领域,卷积操作早已是工程师们耳熟能详的工具。但当我们踏入深度学习的殿堂,面对PyTorch中的nn.Conv1d层时,是否曾疑惑过&a…...
