【Dart 教程系列第 49 篇】什么是策略设计模式?如何在 Dart 中使用策略设计模式

这是【Dart 教程系列第 49 篇】,如果觉得有用的话,欢迎关注专栏。

博文当前所用 Flutter SDK:3.22.1、Dart SDK:3.4.1

文章目录

      • 一:什么是策略设计模式?
      • 二:为什么要使用策略设计模式?(举例说明)
      • 三:如何使用策略设计模式?(举例说明)
        • 3-1:定义抽象策略角色 Strategy
        • 3-2:实现具体的策略角色 Concrete Strategy
        • 3-3:创建环境角色 Context
        • 3-4:创建特定策略对象,并将其传递给环境角色 Context
      • 四:策略模式的优缺点
      • 五:策略模式的其它应用

一:什么是策略设计模式?

策略设计模式是行为型设计模式之一,它在 Gof Book 书中的描述如下:

在计算机编程中,策略模式是一种行为软件设计模式,允许在运行时选择算法。代码不是直接实现单个算法,而是接收运行时指令,决定使用哪一组算法。

标准策略模式的 UML 如下图所示

由上图可以看出

  • Strategy(策略)- 也可以叫抽象策略角色,用以声明一个支持所有算法的接口,并通过 Context 来执行特定策略的方法;
  • Concrete Strategy(具体策略)- 也可以叫具体策略角色,使用 Strategy 接口实现不同的算法。Context 只是使用这个接口,并不关心算法的具体实现;
  • Context(上下文)- 也可以叫环境角色,保存对 Strategy 对象的引用,但不依赖于算法的实现方式。

最后由客户端创建一个特定的策略对象,并将其传递给 Context 即可。

二:为什么要使用策略设计模式?(举例说明)

策略模式允许在运行时选择算法或行为,将算法的使用和实现分离,提高系统的灵活性和可扩展性。

举例说明:

在举例策略模式之前,我们先来看一下,同样的需求,如果不使用策略模式,而是使用一般的 if…else 条件语句来处理会有什么问题。

某视频剪辑类 APP 提供了不同的工具如链接转文字、视频转文字、智能配音和去水印等功能,APP 刚上线为吸引更多的用户,所以允许用户免费使用这些工具。因为目前只有一种免费的支付方式,此时后端定义的获取订单号接口只需要传入工具的 id 就可以了,所以你的代码可能是这样写的。

... tag1
/// 支付状态
Future<bool> payStatus(int toolId) async {// 创建工具订单号final String orderNo = await getToolsOrder(toolId: toolId);// 根据订单号后等待工具处理的结果final bool res = await doSomething(orderNo);return res;
}

后来用户量上来了,APP 内对使用工具做了以下调整。将原先的免费使用工具更改为每天可免费使用某工具一定的次数,免费次数用完后再使用工具需要通过观看广告后才可以。现在的支付方式增加到了两种,为此你定义了一个支付类型的枚举

/// 支付类型
enum PayOrderType {free, // 免费ad, // 看广告
}

后端提供的获取订单号接口也增加了一个支付类型的入参,只针对免费支付类型而言,此时 tag1 代码需要做如下调整

... tag2
Future<bool> payStatus(int toolId, PayOrderType payOrderType) async {if (payOrderType == PayOrderType.free) {final String orderNo = await getToolsOrder(toolId: toolId, payOrderType: PayOrderType.free);final bool res = await doSomething(orderNo);return res;}... 暂时省略部分代码return false;
}

可以对比下 tag1 和 tag2 的代码修改了哪里,不知道你发现什么问题了没,如果没有也没关系,我们继续往下看。

如果是看广告类型的支付方式,调用第三方广告 SDK 时,要求需要传入广告位的标识 key,为此 tag2 的代码不得不再增加一个入参 positionKey,并增加调用观看广告的方法以及是否观看完的判断,如下代码所示。

... tag3
Future<bool> payStatus(int toolId, PayOrderType payOrderType, String positionKey) async {if (payOrderType == PayOrderType.free) {final String orderNo = await getToolsOrder(toolId: toolId, payOrderType: PayOrderType.free);final bool res = await doSomething(orderNo);return res;}else if (payOrderType == PayOrderType.ad) {final String orderNo = await getToolsOrder(toolId: toolId, payOrderType: PayOrderType.ad);// 看广告final ADResult adRes = await openRewardAD(positionKey);if (adRes.code != 1) {return false;}final bool res = await doSomething(orderNo);return res;}return false;
}

这样写也能实现需求,但不知道你发现一个问题没,随着支付方式的增加,payStatus 方法就会不断的根据实际情况增加入参并实现相关支付方式的代码,且不断的增加新的 else if 判断,这违背了六大设计原则之一的 OCP(Open Close Principle)开放封闭原则,也就是对扩展开放,但对修改关闭

我们应该需要这么一种设计理念,当后面再增加新的需求时,应该是在不变动当前正常运行的代码下,通过其他方式新增代码实现新需求。如果为了新需求而改动原有代码,可能会造成其他调用原本代码的地方发生预期之外的错误。

此时,我们的主角策略模式,终于要闪亮登场了。

三:如何使用策略设计模式?(举例说明)

基于目录二的需求,我们通过观察发现,无论是哪种支付方式,我们都是先根据传入的工具 id 创建订单号,然后在工具处理完成后返回结果,所以可以把这个行为抽象出来一个接口,也就是使用策略模式的第一步。

3-1:定义抽象策略角色 Strategy
/// 策略公共接口
abstract class IPayStrategy {Future<bool> payStatus(int toolId);
}
3-2:实现具体的策略角色 Concrete Strategy

对于目录二的免费支付方式而言,具体的实现如下代码所示

/// 免费支付策略
final class PayStrategyByFree implements IPayStrategy {@overrideFuture<bool> payStatus(int toolId) async {final String orderNo = await getToolsOrder(toolId: toolId, payOrderType: PayOrderType.free);final bool res = await doSomething(orderNo);return res;}
}

对于目录二的看广告支付方式而言,具体的实现如下代码所示

/// 看广告支付策略
final class PayStrategyByAD implements IPayStrategy {final String positionKey; // 广告位标识 keyPayStrategyByAD(this.positionKey);@overrideFuture<bool> payStatus(int toolId) async {final String orderNo = await getToolsOrder(toolId: toolId, payOrderType: PayOrderType.ad);// 看广告final ADResult adRes = await openRewardAD(positionKey);if (adRes.code != 1) {return false;}final bool res = await doSomething(orderNo);return res;}
}

可以看出,看广告时所需的广告位标识 key,由支付策略类的构造函数传入。

3-3:创建环境角色 Context

抽象策略和具体策略都已实现,现在创建环境角色 Context,其持有对抽象策略的引用,并决定使用哪种策略。

/// 支付上下文,持有一个策略对象的引用
class PayContext {final IPayStrategy iPayStrategy;PayContext({required this.iPayStrategy});Future<bool> getPayStatus(int toolId) async {return iPayStrategy.payStatus(toolId);}
}
3-4:创建特定策略对象,并将其传递给环境角色 Context

一切的铺垫都已完成,光说不练假把式,现在就让我们把策略模式应用在支付方式上吧。

// 工具是否免费
bool isFree = false;
// 支付策略
late IPayStrategy strategy;
// 免费的支付策略
if (isFree) {strategy = PayStrategyByFree();
} 
// 看广告的支付策略
else {strategy = PayStrategyByAD();
}
// 支付状态(传入具体的支付策略)
final bool success = await PayContext(iPayStrategy: strategy).getPayStatus(123456);
// ... 根据支付状态处理后续业务

这里首先根据条件创建了不同的策略对象,然后把策略对象传递给了环境角色 Context,由环境角色 Context 负责调用具体的策略方法。后面如果再增加其他的支付方式的话,只需要再声明一个策略类并实现具体的算法即可。相比较使用 if…else 的条件语句来说,对外提供了扩展,对内又限制修改,符合 OCP 原则。

至此,关于什么是策略模式以及如何使用策略模式便介绍到这里了。

四:策略模式的优缺点

没有最好的设计模式,只用相对合适的设计模式。策略模式的优缺点如下

优点:

  • 算法可以自由切换
  • 避免使用了多重条件判断
  • 扩展性良好

缺点:

  • 策略类会逐渐增多
  • 所有策略类都需要对外暴露

五:策略模式的其它应用

除了本文举例的支付方式可以使用策略模式外,符合策略模式定义和使用场景的都可以使用该模式。如

  1. 支付选项策略。如支付类型是使用微信、支付宝、信用卡还是银行转账等支付类型。
  2. 游戏伤害计算。如使用不同的招式,技能,为不同的攻击定义不同的算法。
  3. 排序算法。如把不同的算法(冒泡排序、快排排序、选择排序等)通过不同的策略类实现,最终调用公共的 Sort 接口。
  4. 电商平台优惠卷系统。如不同的优惠卷有不同的使用策略,例如满减、打折、买赠等方式。

还有很多其它案例就不一一说明了,知道什么时候该用策略模式以及如何使用策略模式即可,以不变应万变。

你的问题得到解决了吗?欢迎在评论区留言。

赠人玫瑰,手有余香,如果觉得文章不错,希望可以给个一键三连,感谢。


结束语

技术是一点一点积累的,大神也不是一天就可以达到的。原地不动就是退步,所以每天进步一点点。

最后,附上一句格言:"好学若饥,谦卑若愚",望共勉。

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

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

相关文章

UG NX2406 安装教程

软件介绍 UG是一个交互式CAD/CAM(计算机辅助设计与计算机辅助制造)系统&#xff0c;它功能强大&#xff0c;可以轻松实现各种复杂实体及造型的建构。 它在诞生之初主要基于工作站&#xff0c;但随着PC硬件的发展和个人用户的迅速增长&#xff0c;在PC上的应用取得了迅猛的增长…

西蒙学习法

西蒙学习法 一根筋&#xff0c;挖死坑&#xff1b;会思考&#xff0c;持续不断的思考&#xff1b;会问问题&#xff0c;有深度的问题&#xff1b;一直想一个问题的解决办法&#xff1b; 资料 《世界十大学习方法》之西蒙学习法

【HarmonyOS】应用推送使用个推SDK如何实现?

【HarmonyOS】应用推送使用个推SDK如何实现&#xff1f; 前言 个推和极光都是市面上很成熟的推送第三方SDK了。今天讲讲个推SDK在鸿蒙中如何集成使用。 存在即合理&#xff0c;三方SDK推送给我们带来了极大的好处&#xff0c;首先在服务器后台处理一套API就可搞定&#xff0…

Matlab 命令行窗口默认输出(异常)

目录 前言Matlab 先验知识1 异常输出的代码2 正常输出的代码 前言 在单独调试 Matlab 写的函数时出现不想出现的异常打印值&#xff0c;逐个注释排查才找到是 if elseif else 代码块的问题&#xff0c;会默认打印输出 else 部分第一个返回值的值&#xff08;下方代码中的 P值&…

基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-抄送服务处理

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、因为仿钉钉设计器里抄送人是一个服务任务&#xff0c;所以要根据这个服务任务进行处理 2、前端就是一个抄送&#xff0c;选择人 3、这里用了jeecg的选择人组件 <el-form-item prop…

昇思25天学习打卡营第10天|xiaoyushao

从今天开始做一些实践应用&#xff0c;今天分享的是FCN图像语义分割。 全卷积网络&#xff08;Fully Convolutional Networks&#xff0c;FCN&#xff09;是UC Berkeley的Jonathan Long等人于2015年在Fully Convolutional Networks for Semantic Segmentation一文中提出的用于图…

培养前端工程化思维,不要让一行代码毁了整个程序

看《阿丽亚娜 5 号&#xff08;Ariane 5&#xff09;火箭爆炸》有感。 1、动手写项目之前&#xff0c;先进行全局性代码逻辑思考&#xff0c;将该做的事情&#xff0c;一些细节&#xff0c;统一建立标准&#xff0c;避免为以后埋雷。 2、避免使用不必要或无意义的代码、注释。…

极简Springboot+Mybatis-Plus+Vue零基础萌新都看得懂的分页查询(富含前后端项目案例)

目录 springboot配置相关 依赖配置 yaml配置 MySQL创建与使用 &#xff08;可拿软件包项目系统&#xff09; 创建数据库 创建数据表 mybatis-plus相关 Mapper配置 ​编辑 启动类放MapperScan 启动类中配置 添加config配置文件 Springboot编码 实体类 mapperc(Dao…

【第一篇章】初识XGBoost 揭开神秘面纱

XGBoost发展历程 XGBoost显著优势 XGBoost核心概念 XGBoost&#xff08;eXtreme Gradient Boosting&#xff09;是一种在机器学习领域广泛使用的集成学习算法&#xff0c;特别是在分类、回归和排序任务中表现出色。其基本原理建立在决策树、梯度提升和损失函数优化等核心概念之…

shell-awk命令详解

目录 一.概述 二.工作原理 三.工作流程 1.运行模式 2.运行流程 四.基本语法 1.命令格式 2.常用变量  五.变量类型 1.内建变量 2.内置变量 3.BEGIN END运算  4.awk高级用法 5.awk if语句 6.BEGIN END循环 一.概述 AWK是一种处理文本文件的语言&#xff0c;是一…

2024世界技能大赛某省选拔赛“网络安全项目”B模块--操作系统取证解析

2024世界技能大赛某省选拔赛“网络安全项目”B模块--操作系统取证解析 任务一、操作系统取证解析:总结:任务一、操作系统取证解析: A 集团某电脑系统被恶意份子攻击并控制,怀疑其执行了破坏操作,窃取了集团内部的敏感信息,现请分析 A 集团提供的系统镜像和内存镜像,找到…

国产大模型的逆袭:技术路径的策略与实践

〔探索AI的无限可能&#xff0c;微信关注“AIGCmagic”公众号&#xff0c;让AIGC科技点亮生活〕 一.聚焦长文本&#xff0c;国产大模型已有赶超GPT之势 1.1 理科能力差距较大&#xff0c;注重文科能力的提升 整体比较而言&#xff0c;国内大模型与GPT-4&#xff08;官网&…

树与二叉树【数据结构】

前言 之前我们已经学习过了各种线性的数据结构&#xff0c;顺序表、链表、栈、队列&#xff0c;现在我们一起来了解一下一种非线性的结构----树 1.树的结构和概念 1.1树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一…

【计算机网络】ICMP报文实验

一&#xff1a;实验目的 1&#xff1a;掌握ICMP报文的各种类型及其代码。 2&#xff1a;掌握ICMP报文的格式。 3&#xff1a;深入理解TTL的含义&#xff08;Time to Live&#xff0c;生存时间&#xff09;。 二&#xff1a;实验仪器设备及软件 硬件&#xff1a;RCMS-C服务器…

等级保护测评解决方案

什么是等级保护测评&#xff1f; 网络安全等级保护是指对国家重要信息、法人和其他组织及公民的专有信息以及公开信息和存储、传输、处理这些信息的信息系统分等级实行安全保护&#xff0c;对信息系统中使用的信息安全产品实行按等级管理&#xff0c;对信息系统中发生的信息安全…

小模型狂飙!6家巨头争相发布小模型,Andrej Karpathy:大语言模型的尺寸竞争正在倒退...

过去一周&#xff0c;可谓是小模型战场最疯狂的一周&#xff0c;商业巨头改变赛道&#xff0c;向大模型say byebye~。 OpenAI、Apple、Mistral等“百花齐放”&#xff0c;纷纷带着自家性能优越的轻量化小模型入场。 小模型(SLM)&#xff0c;是相对于大语言模型&#xff08;LLM…

Istio 学习笔记

Istio 学习笔记 作者&#xff1a;王珂 邮箱&#xff1a;49186456qq.com 文章目录 Istio 学习笔记[TOC] 前言一、基本概念1.1 Istio定义 二、Istio的安装2.1 通过Istioctl安装2.2 通过Helm安装 三、Istio组件3.1 Gateway3.2 VirtulService3.2.1 route详解3.2.2 match详解3.2.3…

【前端 02】新浪新闻项目-初步使用CSS来排版

在今天的博文中&#xff0c;我们将围绕“新浪新闻”项目&#xff0c;深入探讨HTML和CSS在网页制作中的基础应用。通过具体实例&#xff0c;我们将学习如何设置图片、标题、超链接以及文本排版&#xff0c;同时了解CSS的引入方式和选择器优先级&#xff0c;以及视频和音频标签的…

【Gin】智慧架构的巧妙砌筑:Gin框架中控制反转与依赖注入模式的精华解析与应用实战(下)

【Gin】智慧架构的巧妙砌筑&#xff1a;Gin框架中控制反转与依赖注入模式的精华解析与应用实战(下) 大家好 我是寸铁&#x1f44a; 【Gin】智慧架构的巧妙砌筑&#xff1a;Gin框架中控制反转与依赖注入模式的精华解析与应用实战(下)✨ 喜欢的小伙伴可以点点关注 &#x1f49d; …

怀旧必玩!重返童年,扫雷游戏再度登场!

Python提供了一个标准的GUI&#xff08;图形用户界面&#xff09;工具包&#xff1a;Tkinter。它可以用来创建各种窗口、按钮、标签、文本框等图形界面组件。 而且Tkinter 是 Python 自带的库&#xff0c;无需额外安装。 Now&#xff0c;让我们一起来回味一下扫雷小游戏吧 扫…