【行为型模式】策略模式

news2025/1/19 8:16:08

文章目录

    • 1、简介
    • 2、结构
    • 3、实现方式
      • 3.1、案例引入
      • 3.2、结构分析
      • 3.3、具体实现
    • 4、对比模板方法模式
    • 5、策略模式优缺点
    • 6、应用场景

1、简介

策略模式(Strategy)是一种设计模式,它允许在运行时根据需要选择算法的行为。这个模式将每个算法封装到一个类中,并使它们可互换。让客户端代码可以独立于算法变化而改变其行为。

策略模式通常应用于需要多种算法进行操作的场景,如排序、搜索、数据压缩等。在这些情况下,不同的算法有不同的优缺点和适用性,因此需要进行选择。

通过使用策略模式,我们可以轻松地切换算法,而无需修改客户端代码。这使得代码更加灵活、可扩展、易于维护,减少了重复的代码,并提高了代码的可读性。

2、结构

策略模式的结构包括以下几个部分:

  1. 抽象策略(Strategy)类:定义所有支持的算法或行为的公共接口或抽象类
  2. 具体策略(Concrete Strategy)类:实现抽象策略接口,提供具体的算法或行为。
  3. 环境(Context)类:持有一个对抽象策略的引用,并且通过该引用调用具体策略类中实现的算法或行为。

在策略模式中,客户端代码仅与环境类及其抽象策略接口交互,无需关心具体实现细节。当需要更改算法或行为时,只需要修改具体策略类即可,而无需修改客户端代码或其他策略类。

策略模式uml

3、实现方式

3.1、案例引入

在支付系统中,我们就可以使用策略模式,针对不同的支付方式封装成不同的策略类。每个策略类负责实现一个特定的支付方式,并提供相应的算法来处理付款。这样,客户端代码就可以通过选择不同的策略类来实现不同的支付方式,而无需了解每种支付方式的具体实现细节。

例如,假设我们有一个电子商务网站,支持信用卡、微信支付和支付宝支付三种支付方式。每种支付方式都有自己的实现方式和规则,但客户端并不需要知道支付方式的具体实现,只需要选择一个支付策略即可。这时候,我们可以使用策略模式来实现支付系统,将每种支付方式封装成一个支付策略类,让客户端根据需要选择不同的支付策略类。

3.2、结构分析

在这个案例中,代码中的各个结构可以对应到策略模式中的不同角色:

  1. Context(上下文):对应代码中的PaymentContext类,负责调用具体支付策略对象的付款方法。

  2. Strategy(策略):对应代码中的PaymentStrategy接口,定义了所有支持的支付方式的公共接口。

  3. ConcreteStrategy(具体策略):对应代码中的CreditCardStrategyWeChatPayStrategyAliPayStrategy类,实现了PaymentStrategy接口,提供了不同的支付方式实现逻辑。

案例uml

3.3、具体实现

针对上述场景,具体实现代码如下:

首先定义一个支付策略接口PaymentStrategy,该接口声明了一个方法pay(double amount)用于处理付款:

public interface PaymentStrategy {
    void pay(double amount);
}

接着定义三个具体的支付策略类,分别是CreditCardStrategyWeChatPayStrategyAliPayStrategy。每个类实现了PaymentStrategy接口,并提供了自己的付款实现方式:

/** 
 * 信用卡
 */
public class CreditCardStrategy implements PaymentStrategy {
    /** 信用卡卡号 */
    private String cardNumber;
    /** 信用卡有效期 */
    private String expiryDate;
    /** 信用卡CVV码 */
    private String cvv;

    public CreditCardStrategy(String cardNumber, String expiryDate, String cvv) {
        this.cardNumber = cardNumber;
        this.expiryDate = expiryDate;
        this.cvv = cvv;
    }

    @Override
    public void pay(double amount) {
        // 基于信用卡的付款逻辑
        System.out.println("使用信用卡支付 " + amount + " 元");
    }
}

/** 
 * 微信
 */
public class WeChatPayStrategy implements PaymentStrategy {
    /** 微信openId */
    private String openId;

    public WeChatPayStrategy(String openId) {
        this.openId = openId;
    }

    @Override
    public void pay(double amount) {
        // 基于微信支付的付款逻辑
        System.out.println("使用微信支付 " + amount + " 元");
    }
}

/** 
 * 支付宝
 */
public class AliPayStrategy implements PaymentStrategy {
    private String account;
    private String password;

    public AliPayStrategy(String account, String password) {
        this.account = account;
        this.password = password;
    }

    @Override
    public void pay(double amount) {
        // 基于支付宝的付款逻辑
        System.out.println("使用支付宝支付 " + amount + " 元");
    }
}

最后定义一个支付上下文类PaymentContext,该类负责根据客户端选择的支付方式创建对应的支付策略对象,并调用其付款方法:

public class PaymentContext {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void pay(double amount) {
        if(paymentStrategy == null) {
            throw new IllegalArgumentException("支付策略不能为空");
        }
        paymentStrategy.pay(amount);
    }
}

客户端代码可以根据需要选择不同的支付方式,例如:

// 创建一个信用卡支付策略对象
CreditCardStrategy creditCardStrategy = new CreditCardStrategy("1234567890123456", "2023-12", "123");
// 创建一个微信支付策略对象
WeChatPayStrategy weChatPayStrategy = new WeChatPayStrategy("wxopenid123456");
// 创建一个支付宝支付策略对象
AliPayStrategy aliPayStrategy = new AliPayStrategy("alipayaccount@aliyun.com", "alipaypassword");

// 创建一个支付上下文对象
PaymentContext paymentContext = new PaymentContext();

// 使用信用卡支付
paymentContext.setPaymentStrategy(creditCardStrategy);
paymentContext.pay(100.0);

// 使用微信支付
paymentContext.setPaymentStrategy(weChatPayStrategy);
paymentContext.pay(200.0);

// 使用支付宝支付
paymentContext.setPaymentStrategy(aliPayStrategy);
paymentContext.pay(300.0);

这个示例演示了如何使用策略模式来实现支付系统。客户端代码可以根据需要选择不同的支付策略,而无需关心具体的支付方式实现细节。同时,新的支付方式也可以很方便地添加到系统中,只需要新增一个实现了PaymentStrategy接口的类即可。

运行结果如下:

使用信用卡支付 100.0 元
使用微信支付 200.0 元
使用支付宝支付 300.0

4、对比模板方法模式

模板方法模式和策略模式都属于面向对象编程中的行为型设计模式,它们的目标都是封装算法和行为,以提高代码复用性和可维护性。但它们的实现方式和应用场景有所不同。

  • 模板方法模式:定义了一个算法的骨架,将具体实现延迟到子类中完成,在保持算法结构不变的同时允许子类灵活地实现算法的具体步骤。这种模式常用于处理一些重复性较高的操作,比如在编写一系列相似的程序时,可以使用该模式来避免重复代码的出现;

  • 策略模式:定义了一系列算法族(即一组相似的算法),并将每个算法封装起来,使得它们可以互相替换。这样就能够使得算法的变化独立于使用算法的客户端。这种模式适用于需要在运行时动态选择算法的情况,或者需要对多个相关但不完全相同的算法进行封装的情况。

使用白话文来讲就是:

  • 模板方法模式固定了某一个事件的具体流程,运行拓展的是中间的某一个流程;

  • 策略模式则是针对某一个算法(等同上述的流程)支持拓展。

因此,虽然这两种模式都是用于封装算法和行为,但模板方法模式是基于继承实现的,它只有一个具体的模板类和一些具体的子类,而策略模式则是基于组合实现的,它包含了一组策略类和一个具体的上下文类。两者的应用场景和实现方式不同,需要根据具体需求选择合适的模式来使用。

模板方法模式策略模式
定义定义算法骨架,子类实现具体步骤封装一系列算法,并使其互相替换
实现方式基于继承实现基于组合实现
子类数量通常只有一个抽象模板类和多个具体子类可以有多个具体策略类
调用父类定义算法流程,子类重写具体方法上下文类选择并调用具体策略
目的避免重复代码,保持算法结构不变允许动态选择算法

在开发过程中这两个模式经常配合使用,策略模式通常被用来代替模板方法模式中的部分具体算法

具体而言,在使用模板方法模式时,如果某些具体步骤需要根据特定条件进行选择或者动态替换,就可以把这些步骤视为算法族,并使用策略模式来封装和管理它们。这样可以使得算法选择更加灵活,同时还能够保持模板方法模式的算法结构不变。另外,策略模式还可以在运行时动态地替换算法,不需要修改源代码即可实现。

例如,在一个游戏开发中,我们可以使用模板方法模式定义一个游戏的基本流程,包括游戏开始、游戏进行、游戏结束等步骤。然后,针对不同类型的游戏,我们可以使用策略模式来定义不同的游戏规则,如赛车游戏、飞行游戏等,每种游戏规则都是一种具体的策略。这样,我们就可以根据实际需求来动态地选择并组合不同的游戏规则,从而实现不同类型的游戏。

5、策略模式优缺点

策略模式主要优点和缺点如下:

优点

  1. 策略模式使得各种算法可以在不修改原有代码的情况下替换或者新增,提高了代码的可扩展性和可维护性;
  2. 策略模式可以避免由于多重条件语句导致的代码复杂度增加和可读性降低的问题;
  3. 策略模式将算法的实现从上下文中解耦出来,使得算法可以独立进行单元测试;
  4. 策略模式符合开闭原则,即对扩展开放,对修改关闭,可以通过增加新的策略类来扩展应用,而无需修改原有代码。

缺点

  1. 如果策略数量过多,会导致类数量增加,增加系统的复杂度;
  2. 客户端需要知道所有的策略类,并选择合适的策略类,这可能会导致客户端代码较为复杂;
  3. 策略模式将算法的实现从上下文中解耦出来,同时也意味着上下文不能控制策略的执行顺序,需要客户端自行控制执行顺序。
优点缺点
提高代码的可扩展性和可维护性策略数量过多,增加类数量和系统复杂度
避免由于多重条件语句导致的代码复杂度增加和可读性降低的问题客户端需要知道所有的策略类,并选择合适的策略类,导致客户端代码较为复杂
将算法的实现从上下文中解耦出来,使得算法可以独立进行单元测试上下文不能控制策略的执行顺序,需要客户端自行控制执行顺序
符合开闭原则,即对扩展开放,对修改关闭

6、应用场景

策略模式通常适用于以下场景中:

  1. 系统需要动态地在几种算法中选择一种,或者根据不同的条件选择不同的算法;
  2. 系统中有许多类似的行为,但是具体实现上有所不同,可以使用策略模式将这些行为抽象出来,并定义一个接口或抽象类,然后由具体的实现类来实现这个接口或抽象类;
  3. 一些算法使用了相同的数据,但是实现上有所不同,可以使用策略模式来避免代码重复和代码膨胀,节省代码维护成本。

例如,在电商系统中,针对不同的促销活动,可能会有不同的优惠计算方式,比如满减、打折等。如果使用简单的if-else语句来实现这些计算方式,会导致代码复杂度增加,可读性降低,扩展也不方便。使用策略模式,可以将各种优惠计算方式进行抽象和封装,客户端只需要知道各个策略类的作用,就可以方便地调用它们进行计算。

在Java和Spring中,以下是一些应用了策略模式的例子:

  1. Java中的Collections.sort()方法使用了策略模式。在调用该方法时,可以传递一个Comparator对象作为参数,该对象定义了排序的策略。
  2. 在Spring框架中,JdbcTemplate类使用了策略模式来处理各种不同类型的数据访问操作。JdbcTemplate类接受一个回调对象,该对象提供了执行SQL查询或更新的具体实现。
  3. 在Spring Security框架中,AuthenticationProvider接口使用了策略模式。AuthenticationProvider接口定义了验证用户身份的策略,并且可以同时支持多种不同的身份验证方式。
  4. 在Java中,Thread类的构造函数可以接受一个Runnable对象作为参数,该对象定义了线程运行的策略。
    使用了策略模式来处理各种不同类型的数据访问操作。JdbcTemplate类接受一个回调对象,该对象提供了执行SQL查询或更新的具体实现。

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

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

相关文章

Oracle VM VirtualBox安装开放麒麟桌面版本操作

1.环境 Oracle VM VirtualBox版本6.1.18 开放麒麟桌面版本openkylin 0.0.5 https://mirror.lzu.edu.cn/openkylin-cdimage/yangtze/openkylin-0.9.5-x86_64.iso 1.创建新虚拟电脑 ql 并将ios导入 然后点击启动 注意: vm box如果鼠标设置不当的话 基本上不可能完成…

PEIS源码 体检源码 医院体检系统源码

PEIS体检管理系统源码 PEIS源码 体检源码 医院体检系统源码 本套PEIS医院体检管理系统源码,采用C#语言开发,C/S架构,前台开发工具为Vs2012,后台数据库采用oracle大型数据库。有演示。 文末获取联系 PEIS体检管理系统适用于大中型…

鹅厂狂招工程师,国产自研芯片“沧海”斩获8项世界第一

前言 4月17日,腾讯云官方披露,在由莫斯科国立大学举办的最新一届MSU硬件视频编码比赛中,腾讯自研的编解码芯片“沧海”,经过数月的严格测试,获得了所参加的两个赛道8项评分的全部第一。 MSU为视频压缩领域最具影响力…

TensorFlow-GPU【易安装】(全网最全、通俗易懂、小白友好)

写在前面:CSDN的小伙伴们,很长时间没有发文了,自从靠运气侥幸考上研究生,就一直在苦苦寻找自己的研究方向。在跟风“随大流”之后,选择了深度学习这一领域,也是一场噩梦的开始! 为了更好的学习吴…

MySQL数据恢复-亲测有效版

MySQL数据恢复-亲测有效版 1.日志恢复的前提:1.1.登录远程MySQL服务器:1.2.查看binlog是否开启: 2.查看binlog存放日志文件目录:3.找到mysqlbinlog命令4.设置mysqlbinlog命令为全局可见5.使用mysqlbinlog解析binlog日志6.数据恢复…

信号频谱分析举例

以IQ解调不加滤波器的信号频谱进行分析 系统结构 IQ解调不加滤波器的系统结构框图为: 最后输出的基带复信号时域表达式为: s b b ( t ) s i ( t ) j s q ( t ) s ( t ) c o s ( ω c t ϕ ) − j s ( t ) s i n ( ω c t ϕ ) s_{bb}(t) s_i(t…

UDP报文结构解析

文章目录 UDP报文结构的讲解以及注意事项源端口和目的端口报文长度校验和 UDP报文结构的讲解以及注意事项 想要学习一个协议,我们就需要认识一下这个协议的报文格式,认识这个协议具体是如何组织数据的: 我们常见的UDP报文的格式图都是这样画…

体验编写Vue框架项目实例的详细步骤(包括git仓库使用)

一、查看项目设计图 二、确定项目开发技术栈 vue-cli3 element-ui axios vuex 三、页面布局 四、查看接口文档 五、开始开发 (五).搭建项目结构 1.创建项目 vue create godlike 创建项目的文章在:Vue自主搭建项目:Man…

Unity插件XCharts 图表

参考网址:Unity插件XCharts_xcharts unity_Raki_0的博客-CSDN博客 XCharts 下载地址 :Unity插件XCharts资源-CSDN文库 github 地址:Releases XCharts-Team/XCharts GitHub 一.导入教程 1.直接放入XCharts源码到项目 下载好XCharts源码…

Linux -- Web服务器 快速搭建静态网站,替换默认网页目录

快速搭建静态网站 : 先简单写个 页面 [rootserver ~]# echo " This is my first simple-Web " > /var/www/html/index.html 我们给网页写了一行内容 作为 静态网页的内容 ( 当然了,写的相当简单,您先理解着看&a…

【排序算法 上】带你手撕常见排序 (插入,希尔,选择,堆排序) (动图详解)

欢迎来到 Claffic 的博客 💞💞💞 “东风随春归,发我枝上花。” 前言: 排序是日常生活中极其常见的一种算法,它的功能很简单,就是将数字按照升序/降序排列,最终形成一组有序的数字&a…

Blender3.5 面的操作(一)

目录 1. 面操作1.1 细分面 Subdivide1.2 删除面1.3 挤出面 Extrude1.4 挤出流形1.5 内插面 Inset1.5.1 内插之后选择外侧1.5.2 选择多个面,同时内插操作1.5.3 选择多个面,同时内插选择外侧 1.6 外插1.7 尖分面 Poke Faces1.8 面三角化 Triangulate Faces…

【案例教程】R语言在气象、水文中数据处理及结果分析、绘图实践技术

语言是一门由统计学家开发的用于统计计算和作图的语言(a Statistic Language developed for Statistic by Statistician),由S语言发展而来,以统计分析功能见长。R软件是一款集成了数据操作、统计和可视化功能的优秀的开源软件。来…

分布式对象存储服务minio安装和部署

一、服务器安装minio 1.进行下载 下载地址: https://dl.min.io/server/minio/release/linux-amd64/minio2.新建minio安装目录,执行如下命令 mkdir -p /home/minio/data 把二进制文件上传到安装目录后,执行: chmod x minio //…

(leetcode)20. 有效的括号 13. 罗马数字转整数 14. 最长公共前缀

目录 20. 有效的括号 思路 代码 13. 罗马数字转整数 思路 代码 14. 最长公共前缀 思路 代码 20. 有效的括号 给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字符串需满足&…

使用element-plus组件,修改date-picker默认样式

使用深度选择器来修改子组件的样式&#xff0c;今天遇到一个需求&#xff0c;在el-drawer中嵌入的el-date-picker&#xff0c;再一次总结一下深度选择器的用法&#xff0c;需求如下&#xff1a; template内容&#xff1a; <el-drawer size"70%" v-model"dr…

ZVL3网络分析仪

ZVL3 Rohde&Schwarz ZVL3 3G矢量网络分析仪|罗德与施瓦茨 9KHz至3GHz 罗德与施瓦茨Rohde&Schwarz 性能特点&#xff1a; 频率范围 9kHz至3GHz/6 GHz(典型值为5kHz) 测量时间(201个测量点&#xff0c;以校准的双端口) <75ms 数据传输(201个测量点) 在100Mbit/sLAN…

自主机器人概述

自主机器人概述 自主机器人自主机器人应用案例自主机器人相关实验室 自主机器人 自主机器人定义&#xff1a;自主机器人就是一个机器人可以在没有外界干扰的情况下&#xff0c;具备高自动化程度&#xff0c;可以执行任务的机器人。 自主机器人组成&#xff1a;感知-规划-控制…

福建游乐园运营商金生游乐申请纳斯达克IPO上市,募资900万美元

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;来自福建南平的游乐园运营商金生游乐集团Golden Heaven Group Holdings Ltd &#xff0c;&#xff08;以下简称金生游乐&#xff09;&#xff0c;近期已向美国证券交易委员会&#xff08;SEC&…

智能面板小程序如何实现跨端开发,并无缝引入ChatGPT?

如何让开发者更便捷高效地开发面板小程序&#xff1f; 全球化 IoT 开发平台服务商涂鸦智能&#xff08;NYSE&#xff1a;TUYA&#xff0c;HKEX&#xff1a;2391&#xff09;原先提供的是一套基于 React Native (简称 RN) 的面板 SDK&#xff0c;但是随着面板规模的不断增长&am…