【设计模式】行为型模式(二):策略模式、命令模式
行为型模式(二):策略模式、命令模式
- 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…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...