当前位置: 首页 > news >正文

面向对象设计模式——策略模式

策略设计模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时选择算法的行为。该模式定义了一系列算法,将每个算法封装到一个独立的类中,使它们可以相互替换。策略模式使算法独立于客户端而变化,客户端可以根据需要选择不同的算法。

主要组成部分:

  1. 策略接口(Strategy Interface):定义了一组算法的通用接口,所有具体策略类都要实现这个接口。

  2. 具体策略类(Concrete Strategies):实现策略接口,提供不同的算法实现。

  3. 上下文(Context):维护一个对策略接口的引用,允许客户端在运行时选择算法。上下文通常会将客户端的请求委派给具体策略对象。

应用场景:

  1. 动态选择算法:策略模式适用于需要在运行时根据不同情况切换算法的情况。例如,排序算法,日志记录级别,数据校验等。

  2. 消除大量的条件语句:当一个类有很多条件语句来选择不同的行为时,策略模式可以减少这些条件语句,使代码更具可读性和可维护性。

  3. 复用性:策略模式可以促进算法的复用,不同的上下文可以共享相同的策略对象,减少重复编码。

  4. 解耦合:策略模式将算法的实现与调用代码解耦,这意味着对算法的更改不会影响调用它的代码。

示例:

考虑一个电商平台的购物车系统。不同用户可能有不同的折扣策略,例如普通用户、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);}
}

在这个示例中,不同用户类型对应于不同的具体策略类。购物车上下文可以根据用户类型选择不同的策略,而不需要改变购物车的代码。这使得系统更加灵活,并且可以轻松应对不同的折扣需求。

策略模式是如何消除条件语句的?

策略模式消除条件语句的关键在于将不同的行为封装到各个具体策略类中,而不再需要在上下文类(调用者)中使用大量的条件语句来选择不同的行为。这通过以下方式实现:

  1. 抽象策略接口:首先,策略模式定义了一个抽象策略接口,该接口声明了一组通用的方法,这些方法将被不同的具体策略类实现。这些方法代表不同的行为或算法。

  2. 具体策略类:每个具体策略类都实现了策略接口中的方法,提供了特定的行为或算法实现。每个具体策略类代表了一种行为的变体。

  3. 上下文类:上下文类包含一个策略接口的引用,客户端可以将不同的策略对象传递给上下文类。上下文类使用策略对象执行特定的行为,而不需要了解具体策略的细节。

通过上述步骤,策略模式将条件语句的选择逻辑从客户端移到了上下文类中,客户端只需要选择合适的策略对象传递给上下文,而不再需要编写大量的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 方法中包含条件语句。策略模式将不同的支付逻辑封装在具体策略类中,使代码更加可维护和可读。

为什么说算法可以相互替换?

是的,不同的行为在策略模式中通常对应不同的算法。策略模式的目标之一是将不同的算法封装到各个独立的策略类中,以使它们可以相互替换,这是因为这些算法执行的是相同的任务,但它们可以使用不同的方法来完成。

策略模式的关键在于实现了开闭原则,即对扩展开放,对修改关闭。这意味着可以轻松地添加新的策略(算法),而无需修改已有的代码。这种灵活性使不同的算法可以相互替换,而不会影响上下文类或客户端的代码。

举例来说,考虑一个排序算法的上下文。在这个上下文中,可以有多个策略(算法)来执行不同的排序任务,如冒泡排序、快速排序、归并排序等。每个排序算法都被封装在一个具体的策略类中。如果要更改排序算法,只需创建一个新的策略类并将其传递给上下文类,而不需要修改已有的排序上下文的代码。

策略模式的目的是提供一种简单的方式来交换算法,以满足不同的需求,同时保持代码的可维护性和可扩展性。这种灵活性使算法可以相互替换,而不会对系统的其他部分产生负面影响。

策略模式在应用场景下会和哪些其他设计模式一起使用?

策略模式通常可以与以下设计模式一起使用:

  1. 工厂模式:用于创建具体策略对象的工厂方法,以避免直接实例化具体策略类。

  2. 单例模式:可以用来确保策略对象是唯一的,尤其是在多个地方需要使用相同的策略时。

  3. 装饰器模式:允许你在不修改策略对象的情况下动态地添加额外的行为或功能。

  4. 观察者模式:可以通过观察者模式通知其他对象有关策略的变化,以便它们可以适应新的策略。

策略模式通常用于解耦算法的定义和使用,使系统更具弹性和可维护性。
让我们以一个示例来说明如何将策略模式与其他设计模式一起使用。

场景:假设我们有一个电商平台,我们需要实现一个促销策略,用户可以根据促销策略来获得不同的折扣。这里,我们使用策略模式来处理促销策略的不同算法,而工厂模式用于创建策略对象,单例模式确保策略对象的唯一性,装饰器模式用于动态添加额外的促销信息,观察者模式用于通知用户有关促销策略的变化。

首先,我们定义策略接口:

// 策略接口
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);}}
}

这个示例展示了如何将策略模式与工厂模式、单例模式、装饰器模式和观察者模式一起使用,以处理促销策略的不同算法,确保策略对象的唯一性,动态添加额外信息,并通知用户有关促销策略的变化。

相关文章:

面向对象设计模式——策略模式

策略设计模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许在运行时选择算法的行为。该模式定义了一系列算法&#xff0c;将每个算法封装到一个独立的类中&#xff0c;使它们可以相互替换。策略模式使算法独立于客户端而变化&#xff0c;客…...

Kubernetes - Ingress HTTP 负载搭建部署解决方案(新版本v1.21+)

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

刚刚:腾讯云3年轻量2核2G4M服务器优惠价格366元三年

腾讯云3年轻量2核2G4M服务器&#xff0c;2023双十一优惠价格366元三年&#xff0c;自带4M公网带宽&#xff0c;下载速度可达512KB/秒&#xff0c;300GB月流量&#xff0c;50GB SSD盘系统盘&#xff0c;腾讯云百科txybk.com分享腾讯云轻量2核2G4M服务器性能、优惠活动、购买条件…...

`include指令【FPGA】

案例&#xff1a; 在Verilog中&#xff0c;include指令可以将一个文件的内容插入到当前文件中。 这个指令通常用于将一些常用的代码片段或者模块定义放在单独的文件中&#xff0c; 然后在需要使用的地方通过include指令将其插入到当前文件中。 这样可以提高代码的复用性和可维…...

iphone备份后怎么转到新手机,iphone备份在哪里查看

iphone备份会备份哪些东西&#xff1f;iphone可根据需要备份设备数据、应用数据、苹果系统等。根据不同的备份数据&#xff0c;可备份的数据类型不同&#xff0c;有些工具可整机备份&#xff0c;有些工具可单项数据备份。本文会详细讲解苹果手机备份可以备份哪些东西。 一、ip…...

JAVA毕业设计106—基于Java+Springboot的外卖系统(源码+数据库)

基于JavaSpringboot的外卖系统(源码数据库)106 一、系统介绍 本系统分为用户端和管理端角色 前台用户功能&#xff1a; 登录、菜品浏览&#xff0c;口味选择&#xff0c;加入购物车&#xff0c;地址管理&#xff0c;提交订单。 管理后台&#xff1a; 登录&#xff0c;员工管…...

SpringCore完整学习教程4,入门级别

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

如何能在项目具体编码实现之前能尽可能早的发现问题并解决问题

在项目的具体编码实现之前尽可能早地发现并解决问题&#xff0c;可以大大节省时间和资源&#xff0c;提高项目的成功率。以下是一些策略和方法&#xff1a; 1. 明确需求和预期&#xff1a; 确保所有的项目需求都是清晰和明确的。需求模糊不清是项目失败的常见原因之一。与利益…...

Windows server服务器允许多用户远程的设置

在Windows Server上允许多用户同时进行远程桌面连接&#xff0c;您需要配置远程桌面服务以支持多用户并确保许可证和授权允许多用户连接。以下是在Windows Server上允许多用户远程桌面连接的步骤&#xff1a; 注意&#xff1a;这些步骤适用于 Windows Server 2012、Windows Ser…...

Vmware下的虚拟机NAT连接后仍然木有网络

问题描述 出现在主机能ping通&#xff0c;互联网ping不通的情况。 废话 假设已经设置了网络配置文件IPADDR。 那么&#xff0c;NAT后可以访问互联网的前提是&#xff1a;这个IPADDR的网段在Vmware软件设置的网段内。 解决 在Vmware虚拟网络设置选项卡中&#xff0c;进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容器里面的代码被称为…...

管理类联考——数学——汇总篇——知识点突破——数据分析——记忆

文章目录 考点记忆/考点汇总——按大纲 整体目录大纲法记忆宫殿法绘图记忆法 局部数字编码法对号不对号 归类记忆法重点记忆法歌决记忆法口诀&#xff1a;加法分类&#xff0c;类类相加&#xff1b;乘法分步&#xff0c;步步相乘。 谐音记忆法涂色 理解记忆法比较记忆法转图像记…...

springboot+mybatis-plus实现读写分离

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

java将list转为逗号隔开字符串,将逗号连接的字符串转成字符数组,​将逗号分隔的字符串转换为List​(Java逗号分隔-字符串与数组相互转换)

一、通过testList.stream().collect(Collectors.joining(",")) &#xff0c;通过流转换&#xff0c;将list转为逗号隔开字符串 List<String> testList new ArrayList<>(); testList.add("test1"); testList.add("test2"); testList…...

2023高频前端面试题-CSS

1. CSS 选择器的优先级是怎么样的&#xff1f; CSS 选择器的优先级顺序&#xff1a; 内联样式 > ID选择器 > 类选择器 > 标签选择器 优先级的计算&#xff1a; 优先级是由 A、B、C、D 四个值来决定的&#xff0c;具体计算规则如下 A{如果存在内联样式则为 1&…...

我会在以下情况用到GPT

ChatGPT可以在各种情况下派上用场&#xff0c;包括但不限于以下情况&#xff1a; 获取信息&#xff1a;你可以使用ChatGPT来获取关于各种主题的信息&#xff0c;例如历史事件、科学知识、文化背景等。ChatGPT可以用作一个知识库&#xff0c;回答你的问题。 学习新知识&#xf…...

33:深入浅出x86中断机制

背景 我们知道使用0x10号中断&#xff0c;可以在屏幕上打印一个字符。 问题 系统中的 中断 究竟是什么&#xff1f; 生活中的例子 来看一个生活中例子&#xff1a; 小狄的工作方式 在处理紧急事务的时候&#xff0c;不回应同事的技术求助。老板的召唤必须回应&#xff0c;…...

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是用来构建镜像的&#xff0c;他实际上就是把在linux下的命令操作写到了Dockerfile中&#xff0c;通过Dockerfile去执行设置好的操作命令&#xff0c;保证通过Dockerfile的构建镜像是一致的。 实战分析 该例子来自于 chromium 项目 主要干的事情&#xf…...

机器学习-模型评估与选择

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

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

C++实现分布式网络通信框架RPC(2)——rpc发布端

有了上篇文章的项目的基本知识的了解&#xff0c;现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...