【设计模式】行为型模式(二):策略模式、命令模式
行为型模式(二):策略模式、命令模式
- 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…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...