一文搞懂设计模式—策略模式

本文已收录至Github,推荐阅读 👉 Java随想录

微信公众号:Java随想录

文章目录

    • 使用场景
    • 策略模式实现
    • 策略模式的优缺点
    • 策略模式优化
      • 使用Map取消 Context 类
      • 策略枚举解决策略类膨胀
      • SpringBoot中的策略模式
    • 总结

在软件开发中,经常会遇到需要根据不同的条件来实现不同行为的场景。这种场景下,策略模式(Strategy Pattern)就是一种非常有用的设计模式。

策略模式属于行为型模式,允许我们定义一系列算法,并将其封装在独立的策略类中,使得它们可以互相替换。通过使用策略模式,我们能够灵活地选择和切换不同的算法,而无需修改原有的代码,替代⼤量 if else 的逻辑。

使用场景

策略模式通常在以下情况下被使用:

  • 当存在多种实现方式,且需要在运行时动态选择具体实现时,策略模式非常有用。例如,一个购物应用可能需要根据用户的会员等级来计算折扣,不同等级对应不同的计算方式,这时就可以使用策略模式来实现。
  • 当存在一组类似的行为,只是实现细节略有不同,但又不希望通过继承来添加新的子类时,策略模式也很适用。它将这组行为封装在独立的策略类中,并通过委托的方式在上下文对象中使用。

例如:

  • 支付方式选择:一个电子商务平台可以根据用户的选择来使用不同的支付策略,例如信用卡支付、支付宝支付、微信支付等。
  • 排序算法选择:一个排序工具可以根据用户的需求选择不同的排序算法,例如快速排序、归并排序等。
  • 数据验证:一个表单验证工具可以根据不同的验证规则采用不同的验证策略,例如长度验证、格式验证等。

这些只是策略模式的一些例子,实际应用场景非常丰富。通过使用策略模式,我们可以将算法或行为与具体的业务逻辑解耦,使得系统更加灵活和可扩展。

策略模式实现

在策略模式中,有三个核心角色:上下文(Context)、策略接口(Strategy)和具体策略类(Concrete Strategy)。

  • 上下文(Context):封装了具体策略的执行逻辑,提供给客户端使用的接口。上下文通常包含一个指向策略接口的引用,用于调用具体策略的方法。
  • 策略接口(Strategy):定义了一组算法或行为的公共接口,所有具体策略都必须实现该接口。
  • 具体策略类(Concrete Strategy):实现了策略接口,提供了具体的算法或行为。

下面我们来实现一下策略模式:

步骤 1

创建策略接口。

//策略接口
public interface PaymentStrategy {void pay(double amount);
}

步骤2

创建策略接口实现类。

//具体策略类
public class CreditCardPayment implements PaymentStrategy {public void pay(double amount) {System.out.println("使用信用卡支付:" + amount);// 具体的支付逻辑}
}
public class WeChatPay implements PaymentStrategy {public void pay(double amount) {System.out.println("使用微信支付:" + amount);// 具体的支付逻辑}
}

注意:在实际项目中,我们一般通过工厂方法模式来实现策略类的声明。

实现关系如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

步骤 3

创建 Context 类。

// 上下文类
public class PaymentContext {private PaymentStrategy paymentStrategy;public PaymentContext(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}public void pay(double amount) {paymentStrategy.pay(amount);}
}

调用一下:

// 使用示例
public class Main {public static void main(String[] args) {PaymentStrategy strategy = new CreditCardPayment();PaymentContext context = new PaymentContext(strategy);context.pay(100.0);strategy = new WeChatPay();context = new PaymentContext(strategy);context.pay(200.0);}
}

输出:

使用信用卡支付:100.0
使用微信支付:200.0

在上面的代码中,我们定义了一个 PaymentStrategy 接口作为策略接口,两个具体的策略类 CreditCardPaymentWeChatPay 实现了该接口。然后,我们创建了一个 PaymentContext 上下文对象,并根据需要传入不同的策略实例进行支付操作。

策略模式的优缺点

策略模式的优点包括:

  • 松耦合:策略模式将不同的策略封装在独立的类中,与上下文对象解耦,增加了代码的灵活性和可维护性。
  • 易于扩展:可以通过添加新的策略类来扩展系统的功能,无需修改现有代码。
  • 符合开闭原则:对于新的策略,无需修改上下文对象,只需要实现新的策略接口即可。

策略模式的缺点包括:

  • 类数量增多:每个具体策略都需要一个独立的类,如果策略较多,将导致类的数量增加。
  • 上层必须知道所有策略类:上层模块必须知道有哪些策略,并选择合适的策略进行使用,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么 意义?这是原装策略模式的一个缺点。

注意事项: 如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题,否则日后的系统维护就会成为一个烫手山芋。

策略模式优化

使用Map取消 Context 类

我们可以将策略实现类放进 Map 中,根据 key 去选择具体的策略,就不必事先定义 Context 类。

public static void main(String[] args) {Map<String, PaymentStrategy> map=new HashMap<>();map.put("CREDIT_CARD", new CreditCardPayment());map.put("WECHAT_PAY",new WeChatPay());map.get("CREDIT_CARD").pay(100.0);map.get("WECHAT_PAY").pay(200.0);}    

策略枚举解决策略类膨胀

策略枚举可以解决策略类过多的问题。

我们对原装的策略模式进行改造,把原有定义在抽象策略中的方法移植到枚举中,让枚举成员成为一个具体策略。

@Slf4j
public enum PaymentStrategyEnum {CREDIT_CARD {@Overridepublic void pay(double amount) {log.info("使用信用卡支付:" + amount);// 具体的支付逻辑}},WECHAT_PAY {@Overridepublic void pay(double amount) {log.info("使用微信支付:" + amount);// 具体的支付逻辑}};public abstract void pay(double amount);
}

在上面的代码中,我们定义了一个枚举类型 PaymentStrategy,其中包含两个枚举常量 CREDIT_CARDWECHAT_PAY。每个枚举常量都重写了 pay() 方法,用于具体的支付逻辑。

// 使用示例
public static void main(String[] args) {Map<String, PaymentStrategyEnum> map=new HashMap<>();map.put("CREDIT_CARD",  PaymentStrategyEnum.CREDIT_CARD);map.put("WECHAT_PAY", PaymentStrategyEnum.WECHAT_PAY);map.get("CREDIT_CARD").pay(100.0);map.get("WECHAT_PAY").pay(200.0);}

注意:策略枚举是一个非常优秀和方便的模式,但是它受枚举类型的限制,每个枚举项都是 public、final、static 的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。

SpringBoot中的策略模式

SpringBoot中使用策略模式更加方便:

public interface Test {void print(String name);
}
@Service("testA")
@Slf4j
public class TestA implements Test{@Overridepublic void print(String name) {log.info("实现类A"+name);}
}
@Service("testB")
@Slf4j
public class TestB implements Test{@Overridepublic void print(String name) {log.info("实现类B"+name);}
}

使用的时候 @Autowired 或者 @Resource 即可,SpringBoot会帮我们把实现类自动注入注入Map。

@Resource
private Map<String,Test> map;
Test test = map.get("你想拿出的具体策略类");
test.print("hello world");

总结

策略模式是一种强大而灵活的设计模式,它可以帮助我们处理不同的算法或行为,并使系统更具可维护性和扩展性。通过封装具体的策略类和使用上下文对象,我们可以轻松地选择和切换不同的策略,而无需修改现有的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653708.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pcl应用八叉树实例

pcl应用八叉树实例 文章目录 pcl应用八叉树实例1、基本概念2、基于八叉树的空间划分及搜索操作2.1、关键函数说明2.1.2 OctreePointCloudSearch 类2.1.2 voxelSearch 函数 3、无序点云数据集的空间变化检测 1、基本概念 八叉树结构通过循环递归的划分方法对大小为2 n ∗ 2 n ∗…

C++面试宝典第25题:阶乘末尾零的个数

题目 给定一个整数n,返回n!(n的阶乘)结果尾数中零的个数。 示例 1: 输入:3 输出:0 解释:3! = 6,尾数中没有零。 示例 2: 输入:5 输出:1 解释:5! = 120,尾数中有1个零。 解析 这道题主要考察应聘者对于数学问题的分析和理解能力,以及在多个解决方案中,寻求最优…

elementUI的el-select传递item对象或其他参数的2种方法

方法1 :value“item” 绑定对象 只要:value绑定item对象就可以 value-key"value" 必须是item里的一个属性&#xff0c;绑定值为对象类型时必填 <el-select v-model"value" placeholder"请选择" value-key"value" change"cha…

Qt/QML编程之路:qml通过C++传递变量给另一个qml(42)

Qml的项目设计时,不可避免涉及到qml之间的调用,此时很正常想象到的是如何进行事件传输和参数传输。一般就是在qml文件中定义property int, 或者定义 signal mySignal。 我就遇到了这么一种情况,需要把一个image的source,也就是图片的路径信息,从设置界面直接传递到要设置背…

[EFI]戴尔T5810电脑 Hackintosh 黑苹果efi引导文件

硬件型号驱动情况主板 戴尔T5810&#xff0c;C610/612芯片 处理器英特尔至强E5-2620 v3已驱动内存12 GB已驱动硬盘500GB WD Blue Solid State Drive & 2TB Seagate Mobile Hard Drive (Upgraded)已驱动显卡RX 570 4Gb已驱动声卡瑞昱 Realtek ALC662 英特尔 High Definitio…

(蓝桥杯每日一题)求最长回文串

问题描述 给出一个长度为 n 的小写字符串&#xff0c;求一个最长的子串 S&#xff0c;满足SXY,X&#xff0c;Y>1&#xff0c;且X,Y 均为回文串。 输入格式 输入包括一行: 第一行是一个长度为 n 的小写字符串。 输出格式 输出包括一行&#xff1a; 一行一个整数&#xff0c;表…

Uboot中ARMV7和ARMV8 MMU配置

问题概述 Uboot中如果打开MMU&#xff0c;则MMU需要配置MMU table来管理不同的地址空间。 其中ARMV7和ARMV8中这部分的配置代码是不同。 ARMV7的配置过程 代码参考&#xff1a;u-boot-2020.04/arch/arm/lib/cache-cp15.c mmu_setup →set_section_dcache →dram_bank_mmu_…

【GPU驱动开发】-Mesa ST和GLSL编译器衔接交互分析

前言 不必害怕未知&#xff0c;无需恐惧犯错&#xff0c;做一个Creator&#xff01; &#xff08;基于Mesa 22.2.5版本&#xff09; Mesa State Tracker 与 GLSL 编译器的协同工作是 Mesa 3D 图形渲染管线中的关键环节。这两者的衔接确保了 OpenGL API 调用能够正确、高效地…

Shell脚本转发activemg topic消息到另个activemg服务器

如果你想通过Shell脚本将ActiveMQ中的Topic消息从一个服务器转发到另一个服务器&#xff0c;你可以使用stomp命令行工具来实现。 以下是一个示例脚本&#xff0c;演示如何使用Shell脚本将ActiveMQ中的Topic消息从一个服务器转发到另一个服务器&#xff1a; #!/bin/bash# 源Act…

【论文阅读】Membership Inference Attacks Against Machine Learning Models

基于confidence vector的MIA Machine Learning as a Service简单介绍什么是Membership Inference Attacks&#xff08;MIA&#xff09;攻击实现过程DatasetShadow trainingTrain attack model Machine Learning as a Service简单介绍 机器学习即服务&#xff08;Machine Learn…

Material Components for Android助你打造精美App

Material Components for Android助你打造精美App 简介 Material Components for Android (MDC-Android) 是帮助开发者执行 Material Design 的工具。由谷歌的核心工程师和用户体验设计师团队开发&#xff0c;这些组件使得开发者可以可靠地开发工作流来构建美观且功能齐全的 …

Spring Security 的TokenStore三种实现方式

博主介绍&#xff1a;✌专注于前后端领域开发的优质创作者、秉着互联网精神开源贡献精神&#xff0c;答疑解惑、坚持优质作品共享。本人是掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战&#xff0c;深受全网粉丝喜爱与支持✌有需要可以联系作者我哦&…

期权定价模型系列[9]SABR模型

1.简介 SABR模型是由 Hagan在 2002年提出的一种随机波动率模型&#xff0c;在抛弃了原始的BSM 模型中对于波动率为某一常数的假定&#xff0c;假设隐含波动率同样是符合几何布朗运动的&#xff0c;并且将隐含波动率设定为标的价格和合约行权价的函数&#xff0c;结合了隐含波动…

12.5内存操作流(血干JAVA系列)

12.5内存操作流 12.5内存操作流ByteArraylnputStream类的主要方法ByteArrayOutputStream 类的主要方法【例12.33】使用内存操作流完成一个大写字母转换为小写字母的程序 12.5内存操作流 在 流 的 操 作 中 除 了 进 行 文 件 的 输 入 与 输 出 操 作 之 外 &#xff0c; 也 可…

flask初体验

1、定义 Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。 中文官网 2、初步上手 1、安装flask pip3 install flask 2、创建flask应用 # -*- coding = utf-8 -*- # @Time : 2024/1/28 23:02 # @Author: Frank # @File: main.py…

说说你对vue的mixin的理解,有什么应用场景

mixin是什么 Vue中的mixin 局部混入全局混入注意事项: 使用场景源码分析Vue 的几种类型的合并策略 替换型合并型队列性叠加型小结 此文章&#xff0c;来源于印客学院的资料&#xff0c;这里只是分享&#xff0c;便于查漏补缺。 mixin是什么 Mixin 是 面向对象程序设计语言中…

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测(含优化前后预测可视化)

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测 目录 回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测预测效果基本介绍程序设计参考资料预测效果 <

系统架构设计师-21年-上午试题

系统架构设计师-21年-上午试题 更多软考资料 https://ruankao.blog.csdn.net/ 1 ~ 10 1 前趋图(Precedence Graph)是一个有向无环图&#xff0c;记为:→{(Pi,Pj)|Pi must complete before Pj may strat}&#xff0c;假设系统中进程P{P1&#xff0c;P2&#xff0c;P3&#xf…

爬虫基础-计算机网络协议

一个数据的传输 这些设备的数据转发是通过协议来完成的&#xff0c;整个互联网可以说是完全由网络协议来维持的 不同的协议分工不同&#xff0c;比如ip协议确保了ip寻址&#xff0c;tcp协议确保了数据完整性 IP地址和URL ip地址 整个网络传输可以比作快递&#xff0c;数据就…

使用毫米波雷达传感器的功能安全兼容系统设计指南1(TI文档)

摘要 功能安全标准规定了在系统中实施安全的要求&#xff0c;并有助于概括该系统要达到的安全目标。包括功能安全的系统设计不仅要降低操作不当的风险&#xff0c;还要检测故障并将其影响降到最低。随着汽车和工业系统的自主性越来越强&#xff0c;严格的功能安全要求被强制执行…