面向对象设计模式——策略模式
策略设计模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时选择算法的行为。该模式定义了一系列算法,将每个算法封装到一个独立的类中,使它们可以相互替换。策略模式使算法独立于客户端而变化,客户端可以根据需要选择不同的算法。
主要组成部分:
-
策略接口(Strategy Interface):定义了一组算法的通用接口,所有具体策略类都要实现这个接口。
-
具体策略类(Concrete Strategies):实现策略接口,提供不同的算法实现。
-
上下文(Context):维护一个对策略接口的引用,允许客户端在运行时选择算法。上下文通常会将客户端的请求委派给具体策略对象。
应用场景:
-
动态选择算法:策略模式适用于需要在运行时根据不同情况切换算法的情况。例如,排序算法,日志记录级别,数据校验等。
-
消除大量的条件语句:当一个类有很多条件语句来选择不同的行为时,策略模式可以减少这些条件语句,使代码更具可读性和可维护性。
-
复用性:策略模式可以促进算法的复用,不同的上下文可以共享相同的策略对象,减少重复编码。
-
解耦合:策略模式将算法的实现与调用代码解耦,这意味着对算法的更改不会影响调用它的代码。
示例:
考虑一个电商平台的购物车系统。不同用户可能有不同的折扣策略,例如普通用户、VIP用户、员工用户可以享受不同的折扣。使用策略模式可以创建一个折扣策略接口,然后为每个用户类型创建具体的策略类。购物车上下文对象可以接收不同用户类型的策略,并在结算时应用相应的折扣策略。
这里是一个伪代码示例:
// 策略接口
interface DiscountStrategy {double applyDiscount(double totalAmount);
}// 具体策略类
class RegularCustomerDiscount implements DiscountStrategy {public double applyDiscount(double totalAmount) {return totalAmount;}
}class VIPCustomerDiscount implements DiscountStrategy {public double applyDiscount(double totalAmount) {return totalAmount * 0.9; // 10% off}
}class EmployeeDiscount implements DiscountStrategy {public double applyDiscount(double totalAmount) {return totalAmount * 0.8; // 20% off}
}// 上下文
class ShoppingCart {private DiscountStrategy discountStrategy;public ShoppingCart(DiscountStrategy strategy) {this.discountStrategy = strategy;}public double checkout(double totalAmount) {return discountStrategy.applyDiscount(totalAmount);}
}
在这个示例中,不同用户类型对应于不同的具体策略类。购物车上下文可以根据用户类型选择不同的策略,而不需要改变购物车的代码。这使得系统更加灵活,并且可以轻松应对不同的折扣需求。
策略模式是如何消除条件语句的?
策略模式消除条件语句的关键在于将不同的行为封装到各个具体策略类中,而不再需要在上下文类(调用者)中使用大量的条件语句来选择不同的行为。这通过以下方式实现:
-
抽象策略接口:首先,策略模式定义了一个抽象策略接口,该接口声明了一组通用的方法,这些方法将被不同的具体策略类实现。这些方法代表不同的行为或算法。
-
具体策略类:每个具体策略类都实现了策略接口中的方法,提供了特定的行为或算法实现。每个具体策略类代表了一种行为的变体。
-
上下文类:上下文类包含一个策略接口的引用,客户端可以将不同的策略对象传递给上下文类。上下文类使用策略对象执行特定的行为,而不需要了解具体策略的细节。
通过上述步骤,策略模式将条件语句的选择逻辑从客户端移到了上下文类中,客户端只需要选择合适的策略对象传递给上下文,而不再需要编写大量的if-else语句来决定采用哪种行为。
以下是示例代码,演示如何通过策略模式消除条件语句:
// 抽象策略接口
interface PaymentStrategy {void pay(int amount);
}// 具体策略类
class CreditCardPayment implements PaymentStrategy {public void pay(int amount) {// 实现信用卡支付逻辑}
}class PayPalPayment implements PaymentStrategy {public void pay(int amount) {// 实现PayPal支付逻辑}
}// 上下文类
class ShoppingCart {private PaymentStrategy paymentStrategy;public void setPaymentStrategy(PaymentStrategy strategy) {this.paymentStrategy = strategy;}public void checkout(int amount) {paymentStrategy.pay(amount); // 执行支付,不需要条件语句}
}
在上述示例中,ShoppingCart
上下文类可以使用不同的 PaymentStrategy
对象,无需在 checkout
方法中包含条件语句。策略模式将不同的支付逻辑封装在具体策略类中,使代码更加可维护和可读。
为什么说算法可以相互替换?
是的,不同的行为在策略模式中通常对应不同的算法。策略模式的目标之一是将不同的算法封装到各个独立的策略类中,以使它们可以相互替换,这是因为这些算法执行的是相同的任务,但它们可以使用不同的方法来完成。
策略模式的关键在于实现了开闭原则,即对扩展开放,对修改关闭。这意味着可以轻松地添加新的策略(算法),而无需修改已有的代码。这种灵活性使不同的算法可以相互替换,而不会影响上下文类或客户端的代码。
举例来说,考虑一个排序算法的上下文。在这个上下文中,可以有多个策略(算法)来执行不同的排序任务,如冒泡排序、快速排序、归并排序等。每个排序算法都被封装在一个具体的策略类中。如果要更改排序算法,只需创建一个新的策略类并将其传递给上下文类,而不需要修改已有的排序上下文的代码。
策略模式的目的是提供一种简单的方式来交换算法,以满足不同的需求,同时保持代码的可维护性和可扩展性。这种灵活性使算法可以相互替换,而不会对系统的其他部分产生负面影响。
策略模式在应用场景下会和哪些其他设计模式一起使用?
策略模式通常可以与以下设计模式一起使用:
-
工厂模式:用于创建具体策略对象的工厂方法,以避免直接实例化具体策略类。
-
单例模式:可以用来确保策略对象是唯一的,尤其是在多个地方需要使用相同的策略时。
-
装饰器模式:允许你在不修改策略对象的情况下动态地添加额外的行为或功能。
-
观察者模式:可以通过观察者模式通知其他对象有关策略的变化,以便它们可以适应新的策略。
策略模式通常用于解耦算法的定义和使用,使系统更具弹性和可维护性。
让我们以一个示例来说明如何将策略模式与其他设计模式一起使用。
场景:假设我们有一个电商平台,我们需要实现一个促销策略,用户可以根据促销策略来获得不同的折扣。这里,我们使用策略模式来处理促销策略的不同算法,而工厂模式用于创建策略对象,单例模式确保策略对象的唯一性,装饰器模式用于动态添加额外的促销信息,观察者模式用于通知用户有关促销策略的变化。
首先,我们定义策略接口:
// 策略接口
interface PromotionStrategy {double applyDiscount(double amount);
}
然后,实现具体的促销策略:
// 具体策略类
class BlackFridayPromotion implements PromotionStrategy {public double applyDiscount(double amount) {// 实现 Black Friday 促销算法}
}class ChristmasPromotion implements PromotionStrategy {public double applyDiscount(double amount) {// 实现圣诞促销算法}
}
接下来,使用工厂模式创建策略对象:
// 策略工厂
class PromotionStrategyFactory {private static PromotionStrategy blackFriday = new BlackFridayPromotion();private static PromotionStrategy christmas = new ChristmasPromotion();public static PromotionStrategy getPromotionStrategy(String promotionType) {if ("BlackFriday".equals(promotionType)) {return blackFriday;} else if ("Christmas".equals(promotionType)) {return christmas;}// 其他策略return null;}
}
使用单例模式来确保策略对象的唯一性:
// 单例模式
class PromotionStrategySingleton {private static PromotionStrategy instance;private PromotionStrategySingleton() {// 私有构造函数}public static PromotionStrategy getInstance() {if (instance == null) {instance = PromotionStrategyFactory.getPromotionStrategy("Default"); // 默认策略}return instance;}
}
使用装饰器模式来添加额外的促销信息:
// 装饰器模式
class PromotionDecorator implements PromotionStrategy {private PromotionStrategy strategy;private String additionalInfo;public PromotionDecorator(PromotionStrategy strategy, String additionalInfo) {this.strategy = strategy;this.additionalInfo = additionalInfo;}public double applyDiscount(double amount) {// 添加额外信息的处理return strategy.applyDiscount(amount);}
}
最后,使用观察者模式通知用户有关促销策略的变化:
// 观察者模式
interface PromotionObserver {void updatePromotion(String promotionInfo);
}class User implements PromotionObserver {private String username;public User(String username) {this.username = username;}public void updatePromotion(String promotionInfo) {System.out.println(username + " received promotion info: " + promotionInfo);}
}class PromotionSubject {private List<PromotionObserver> observers = new ArrayList<>();public void addObserver(PromotionObserver observer) {observers.add(observer);}public void notifyObservers(String promotionInfo) {for (PromotionObserver observer : observers) {observer.updatePromotion(promotionInfo);}}
}
这个示例展示了如何将策略模式与工厂模式、单例模式、装饰器模式和观察者模式一起使用,以处理促销策略的不同算法,确保策略对象的唯一性,动态添加额外信息,并通知用户有关促销策略的变化。
相关文章:
面向对象设计模式——策略模式
策略设计模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时选择算法的行为。该模式定义了一系列算法,将每个算法封装到一个独立的类中,使它们可以相互替换。策略模式使算法独立于客户端而变化,客…...

Kubernetes - Ingress HTTP 负载搭建部署解决方案(新版本v1.21+)
在看这一篇之前,如果不了解 Ingress 在 K8s 当中的职责,建议看之前的一篇针对旧版本 Ingress 的部署搭建,在开头会提到它的一些简介Kubernetes - Ingress HTTP 负载搭建部署解决方案_放羊的牧码的博客-CSDN博客 开始表演 1、kubeasz 一键安装…...

刚刚:腾讯云3年轻量2核2G4M服务器优惠价格366元三年
腾讯云3年轻量2核2G4M服务器,2023双十一优惠价格366元三年,自带4M公网带宽,下载速度可达512KB/秒,300GB月流量,50GB SSD盘系统盘,腾讯云百科txybk.com分享腾讯云轻量2核2G4M服务器性能、优惠活动、购买条件…...
`include指令【FPGA】
案例: 在Verilog中,include指令可以将一个文件的内容插入到当前文件中。 这个指令通常用于将一些常用的代码片段或者模块定义放在单独的文件中, 然后在需要使用的地方通过include指令将其插入到当前文件中。 这样可以提高代码的复用性和可维…...

iphone备份后怎么转到新手机,iphone备份在哪里查看
iphone备份会备份哪些东西?iphone可根据需要备份设备数据、应用数据、苹果系统等。根据不同的备份数据,可备份的数据类型不同,有些工具可整机备份,有些工具可单项数据备份。本文会详细讲解苹果手机备份可以备份哪些东西。 一、ip…...

JAVA毕业设计106—基于Java+Springboot的外卖系统(源码+数据库)
基于JavaSpringboot的外卖系统(源码数据库)106 一、系统介绍 本系统分为用户端和管理端角色 前台用户功能: 登录、菜品浏览,口味选择,加入购物车,地址管理,提交订单。 管理后台: 登录,员工管…...

SpringCore完整学习教程4,入门级别
本章从第4章开始 4. Logging Spring Boot使用Commons Logging进行所有内部日志记录,但保留底层日志实现开放。为Java Util Logging、Log4J2和Logback提供了默认配置。在每种情况下,记录器都预先配置为使用控制台输出和可选的文件输出。 默认情况下&…...

如何能在项目具体编码实现之前能尽可能早的发现问题并解决问题
在项目的具体编码实现之前尽可能早地发现并解决问题,可以大大节省时间和资源,提高项目的成功率。以下是一些策略和方法: 1. 明确需求和预期: 确保所有的项目需求都是清晰和明确的。需求模糊不清是项目失败的常见原因之一。与利益…...
Windows server服务器允许多用户远程的设置
在Windows Server上允许多用户同时进行远程桌面连接,您需要配置远程桌面服务以支持多用户并确保许可证和授权允许多用户连接。以下是在Windows Server上允许多用户远程桌面连接的步骤: 注意:这些步骤适用于 Windows Server 2012、Windows Ser…...

Vmware下的虚拟机NAT连接后仍然木有网络
问题描述 出现在主机能ping通,互联网ping不通的情况。 废话 假设已经设置了网络配置文件IPADDR。 那么,NAT后可以访问互联网的前提是:这个IPADDR的网段在Vmware软件设置的网段内。 解决 在Vmware虚拟网络设置选项卡中,进NAT配…...

2.Vue — 模板语法、数据绑定、el与data的写法、数据代理
文章目录 一、模板语法1.1 插值语法1.2指令语法 二、数据绑定语法2.1 单向数据绑定2.2 双向数据绑定 三、el与data的两种写法3.1 el3.2 data 四、数据代理4.1 Object.defineProperty4.2 Vue数据代理4.2.1 展示数据代理4.2.2 Vue数据代理 一、模板语法 root容器里面的代码被称为…...

管理类联考——数学——汇总篇——知识点突破——数据分析——记忆
文章目录 考点记忆/考点汇总——按大纲 整体目录大纲法记忆宫殿法绘图记忆法 局部数字编码法对号不对号 归类记忆法重点记忆法歌决记忆法口诀:加法分类,类类相加;乘法分步,步步相乘。 谐音记忆法涂色 理解记忆法比较记忆法转图像记…...

springboot+mybatis-plus实现读写分离
shigen坚持日更的博客写手,擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。坚持记录和分享从业两年以来的技术积累和思考,不断沉淀和成长。 最近shigen加班也比较严重,很多天文章没有更新了,各位读者和伙伴见…...

java将list转为逗号隔开字符串,将逗号连接的字符串转成字符数组,将逗号分隔的字符串转换为List(Java逗号分隔-字符串与数组相互转换)
一、通过testList.stream().collect(Collectors.joining(",")) ,通过流转换,将list转为逗号隔开字符串 List<String> testList new ArrayList<>(); testList.add("test1"); testList.add("test2"); testList…...

2023高频前端面试题-CSS
1. CSS 选择器的优先级是怎么样的? CSS 选择器的优先级顺序: 内联样式 > ID选择器 > 类选择器 > 标签选择器 优先级的计算: 优先级是由 A、B、C、D 四个值来决定的,具体计算规则如下 A{如果存在内联样式则为 1&…...
我会在以下情况用到GPT
ChatGPT可以在各种情况下派上用场,包括但不限于以下情况: 获取信息:你可以使用ChatGPT来获取关于各种主题的信息,例如历史事件、科学知识、文化背景等。ChatGPT可以用作一个知识库,回答你的问题。 学习新知识…...

33:深入浅出x86中断机制
背景 我们知道使用0x10号中断,可以在屏幕上打印一个字符。 问题 系统中的 中断 究竟是什么? 生活中的例子 来看一个生活中例子: 小狄的工作方式 在处理紧急事务的时候,不回应同事的技术求助。老板的召唤必须回应,…...
docker docker-compose安装(centos7)
docker安装 1.卸载旧版 卸载旧版 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine2.安装一个yum工具 yum install -y yum-utils3.配置docker的yum源 yum-config-manager -…...
Dockerfile文件详细教程
写在前面 Dockerfile是用来构建镜像的,他实际上就是把在linux下的命令操作写到了Dockerfile中,通过Dockerfile去执行设置好的操作命令,保证通过Dockerfile的构建镜像是一致的。 实战分析 该例子来自于 chromium 项目 主要干的事情…...

机器学习-模型评估与选择
文章目录 评估方法留出法交叉验证自助法 性能的衡量回归问题分类问题查准率、查全率与F1ROC与AUC 在机器学习中,我们通常面临两个主要问题:欠拟合和过拟合。欠拟合指模型无法在训练数据上获得足够低的误差,通常是因为模型太简单,无…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...