在 React 中应用设计模式:策略模式

news2025/6/30 6:35:39

这篇文章是关于我们许多人在 React 和前端开发中遇到的一个问题(有时甚至没有意识到这是一个问题):在不同的组件、钩子、实用程序等中实现了一段逻辑。

让我们深入了解问题的详细信息以及如何解决它。正如标题所暗示的,我们将使用策略模式来解决它。

问题:霰弹枪手术

Shotgun Surgery是一种代码味道,其中进行任何修改都需要对许多不同的地方进行许多小的更改


(图片来源:https ://refactoring.guru/smells/shotgun-surgery )

这怎么会发生在一个项目中?假设我们需要为产品实施定价卡,我们根据客户的来源调整价格、货币、折扣策略和消息:

我们大多数人可能会按如下方式实施定价卡:

  • 组件:PricingCardPricingHeaderPricingBody
  • 效用函数:(getDiscountMessageutils/discount.ts中),formatPriceByCurrency(在utils/price.ts 中)。
  • PricingBody组件还计算最终价格。

这是完整的实现:

现在假设我们需要更改一个国家/地区的定价计划,或为另一个国家/地区添加新的定价计划。您将如何处理上述实施?您必须至少修改 3 个地方并向已经凌乱的if-else块添加更多条件:

  • 修改PricingBody组件。
  • 修改getDiscountMessage函数。
  • 修改formatPriceByCurrency函数。

如果您已经听说过 SOLID,那么我们已经违反了前 2 个原则:单一职责原则和开闭原则。

解决方案:策略模式

策略模式非常简单。我们可以简单的理解为我们每个国家的定价方案都是一个策略。在那个策略类中,我们实现了该策略的所有相关逻辑。

假设您熟悉 OOP,我们可以有一个PriceStrategy实现共享/公共逻辑的抽象类 ( ),然后具有不同逻辑的策略将继承该抽象类。PriceStrategy抽象类如下所示:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">Country</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">Currency</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">../../types</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span>

<span style="color:var(--syntax-declaration-color)">abstract</span> <span style="color:var(--syntax-declaration-color)">class</span> <span style="color:var(--syntax-name-color)">PriceStrategy</span> <span style="color:var(--syntax-text-color)">{</span>
  <span style="color:var(--syntax-declaration-color)">protected</span> <span style="color:var(--syntax-name-color)">country</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">Country</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">Country</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">AMERICA</span><span style="color:var(--syntax-text-color)">;</span>
  <span style="color:var(--syntax-declaration-color)">protected</span> <span style="color:var(--syntax-name-color)">currency</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">Currency</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">Currency</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">USD</span><span style="color:var(--syntax-text-color)">;</span>
  <span style="color:var(--syntax-declaration-color)">protected</span> <span style="color:var(--syntax-name-color)">discountRatio</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span>

  <span style="color:var(--syntax-name-color)">getCountry</span><span style="color:var(--syntax-text-color)">():</span> <span style="color:var(--syntax-name-color)">Country</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">country</span><span style="color:var(--syntax-text-color)">;</span>
  <span style="color:var(--syntax-text-color)">}</span>

  <span style="color:var(--syntax-name-color)">formatPrice</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">number</span><span style="color:var(--syntax-text-color)">):</span> <span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">currency</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">toLocaleString</span><span style="color:var(--syntax-text-color)">()].</span><span style="color:var(--syntax-name-color)">join</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">''</span><span style="color:var(--syntax-text-color)">);</span>
  <span style="color:var(--syntax-text-color)">}</span>

  <span style="color:var(--syntax-name-color)">getDiscountAmount</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">number</span><span style="color:var(--syntax-text-color)">):</span> <span style="color:var(--syntax-declaration-color)">number</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">price</span> <span style="color:var(--syntax-error-color)">*</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">discountRatio</span><span style="color:var(--syntax-text-color)">;</span>
  <span style="color:var(--syntax-text-color)">}</span>

  <span style="color:var(--syntax-name-color)">getFinalPrice</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">number</span><span style="color:var(--syntax-text-color)">):</span> <span style="color:var(--syntax-declaration-color)">number</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">price</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">getDiscountAmount</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">);</span>
  <span style="color:var(--syntax-text-color)">}</span>

  <span style="color:var(--syntax-name-color)">shouldDiscount</span><span style="color:var(--syntax-text-color)">():</span> <span style="color:var(--syntax-name-color)">boolean</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">discountRatio</span> <span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span>
  <span style="color:var(--syntax-text-color)">}</span>

  <span style="color:var(--syntax-name-color)">getDiscountMessage</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">number</span><span style="color:var(--syntax-text-color)">):</span> <span style="color:var(--syntax-declaration-color)">string</span> <span style="color:var(--syntax-text-color)">{</span>
    <span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">formattedDiscountAmount</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">formatPrice</span><span style="color:var(--syntax-text-color)">(</span>
      <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">getDiscountAmount</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">)</span>
    <span style="color:var(--syntax-text-color)">);</span>

    <span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-string-color)">`It's lucky that you come from </span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">country</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">, because we're running a program that discounts the price by </span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">formattedDiscountAmount</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">.`</span><span style="color:var(--syntax-text-color)">;</span>
  <span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>

<span style="color:var(--syntax-declaration-color)">export</span> <span style="color:var(--syntax-declaration-color)">default</span> <span style="color:var(--syntax-name-color)">PriceStrategy</span><span style="color:var(--syntax-text-color)">;</span>
</code></span></span>

我们只需将实例化的策略作为 prop 传递给PricingCard组件:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)"><</span><span style="color:var(--syntax-name-color)">PricingCard</span> <span style="color:var(--syntax-name-color)">price</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-literal-color)">7669</span><span style="color:var(--syntax-string-color)">}</span> <span style="color:var(--syntax-name-color)">strategy</span><span style="color:var(--syntax-text-color)">=</span><span style="color:var(--syntax-string-color)">{</span><span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">JapanPriceStrategy</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-string-color)">}</span> <span style="color:var(--syntax-text-color)">/></span>
</code></span></span>

道具PricingCard定义为:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">interface</span> <span style="color:var(--syntax-name-color)">PricingCardProps</span> <span style="color:var(--syntax-text-color)">{</span>
  <span style="color:var(--syntax-text-color)">price</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">number</span><span style="color:var(--syntax-text-color)">;</span>
  <span style="color:var(--syntax-text-color)">strategy</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">PriceStrategy</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>

同样,如果您了解 OOP,那么我们不仅在使用继承,而且还在此处使用多态性。

这是解决方案的完整实现:

让我们再次问同样的问题:我们如何为新的国家/地区添加新的定价计划?使用这个解决方案,我们只需要添加一个新的策略类,而不需要修改任何现有代码。通过这样做,我们也满足了 SOLID。

结论

因此,通过在我们的 React 代码库中检测代码异味——Shotgun Surgery,我们应用了一种设计模式——策略模式——来解决它。我们的代码结构来自于:

对此:

现在我们的逻辑存在于一个地方,不再分布在许多地方。

如果您对设计模式和架构以及如何使用它们来解决前端世界中的问题感兴趣,请务必给我点赞和关注。

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

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

相关文章

DPDK-收包完整过程

本篇博客作为自己了解dpdk收包过程的一个记录。在写时发现已经有很多写DPDK收包过程的博客了&#xff0c;但还是决定自己写一遍。 DPDK收包分为两个阶段&#xff0c;首先是DMA将数据包从网卡搬运到内存&#xff0c;然后是调用dpdk提供的接口rte_eth_rx_burst去取。但是具体是怎…

Oracle和其他数据库有什么区别?从引号开始了解!

无论测试或者开发&#xff0c;对数据库的增删改查都是家常便饭。但有些小知识是经常被忽略&#xff0c;却又不能不去了解的&#xff0c;例如单引号和双引号的用法和区别&#xff0c;看完这一篇&#xff0c;你肯定会有收获。 首先我们要区别一个概念&#xff0c;即单引号(‘)和…

【Java语言】— Java基础02

1.数据类型 &#xff08;1&#xff09;数据类型的作用 数据类型就是约束变量存储数据的形式。 数据类型 变量名称初始值;&#xff08;2&#xff09;数据类型的分类 引用数据类型&#xff08;除基本数据类型之外的&#xff0c;如String&#xff09;基本数据类型:4大类8种。 …

如何从 0 开始学 Python 自动化测试开发(一)

本文是「如何从 0 开始学 Python 自动化测试开发」专题系列文章第一篇&#xff0c;适合零基础入门的同学。 作者方程老师&#xff0c;是前某跨国通信公司高级测试经理&#xff0c;目前为某互联网名企资深测试技术专家&#xff0c;也是霍格沃兹测试学院特邀讲师。有十余年大型电…

项目管理软件怎么选?只需要关注4点

项目管理有许多不同的风格&#xff0c;但无论如何管理项目&#xff0c;根据企业的当前需求和未来发展轨迹选择合适的项目管理软件都很重要。 虽然大多数优秀的项目管理软件都提供相似的功能&#xff0c;但没有两个平台是完全相同的。以下是企业在选择项目管理软件时应该考虑的…

【虹科新闻】虹科与SOSLAB正式建立合作伙伴关系

近日&#xff0c;虹科与SOSLAB正式建立合作伙伴关系&#xff0c;虹科将共同与SOSLAB开展亚太地区市场开发&#xff0c;聚焦于工业领域客户开拓&#xff0c;深入本地技术支持、测试与售后服务落地&#xff0c;为客户提供高效、可靠的激光雷达解决方案。 “虹科很高兴与SOSLAB合作…

锁等待超时

问题背景 今天测试同事发现项目里面大部分接口报错&#xff0c;把日志捞出来看了下出现大量的锁等待超时的错误。 Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transactionat sun.reflect.N…

MULLS: Versatile LiDAR SLAM via Multi-metric Linear Least Square论文阅读

1. 摘要 随着自动驾驶与移动建图的快速发展&#xff0c;实际项目中对现成的激光SLAM建图方案的需求也越来越强烈&#xff0c;并且要求解决方案适用于各种不同规格的激光雷达与各种复杂场景。因此&#xff0c;我们提出了MULLS&#xff0c;一种高效&#xff0c;低漂移&#xff0…

状态观测控制器设计与仿真验证

【无限嚣张&#xff08;菜菜&#xff09;】&#xff1a;hello您好&#xff0c;我是菜菜&#xff0c;很高兴您能来访我的博客&#xff0c;我是一名爱好编程学习研究的菜菜&#xff0c;每天分享自己的学习&#xff0c;想法&#xff0c;博客来源与自己的学习项目以及编程中遇到问题…

深度学习炼丹-数据处理和增强

前言一&#xff0c;Normalization 概述 1.1&#xff0c;Normalization 定义1.2&#xff0c;什么情况需要 Normalization1.3&#xff0c;Data Normalization 方法1.4&#xff0c;示例代码 二&#xff0c;normalize images 2.1&#xff0c;图像 normalization 定义2.2&#xff0c…

[XCTF]halo(2019护网杯)(难度2)

目录 前言 一、题目重述 二、解题思路 1.Base64解密 2.难以想到的异或运算 三、flag 总结 前言 注意&#xff01;攻防世界题目有误&#xff01;&#xff01;给出题目与原题不一样但是答案却和护网杯原题答案一样&#xff01;&#xff01; 一、题目重述 aWdxNDs0NDFSOz…

NVM Express Base Specification 2.0c - 2 Theory of Operation

The interface has the following key attributes: 在命令提交或完成路径中不需要非缓存/ MMIO寄存器读取;在命令提交路径中最多需要一个MMIO寄存器写或一个64B消息;支持多达65,535个I/O队列&#xff0c;每个I/O队列支持多达65,535个未完成的命令;优先级与每个I/O队列相关联&a…

【MySQL】基于InnoDB的数据库索引

文章目录前言1、索引引入2、索引语法2.1、创建索引2.2、查看索引2.3、删除索引2.4、案例引入3、索引结构3.1、概述3.2、引擎支持3.3、BTree3.4、Hash4、索引类别4.1、分类4.2、过程分析5、性能分析5.1、执行频率5.2、慢查询日志5.3、explain6、最左前缀原则7、索引失效7.1、范围…

2023年学一门IT技术的最佳选择就是软件测试!

互联网行业的不断发展&#xff0c;也增加了IT行业的就业机会&#xff0c;作为最适合零基础小白入行、门槛低的软件测试岗位来说&#xff0c;也受到越来越多转行者的关注&#xff0c;但是耳边依然充斥着各种关于这个行业不好的言论&#xff0c;诸如“行业饱和了&#xff0c;学完…

华为OD机试真题 Python 实现【最长连续方波信号】

目录 题目 思路 考点 Code 题目 输入一串方波信号,求取最长的完全连续交替方波信号,并将其输出,如果有相同长度的交替方波信号,输出任一即可, 方波信号高位用1标识,低位用0标识,如图: 说明: 1) 一个完整的信号一定以0开始然后以0结尾,即010是一个完整信号,但10…

【自动化】【autojs】02 autox环境搭建和踩坑

▒ 目录 ▒&#x1f6eb; 导读需求开发环境1️⃣ 环境搭建AutoXadbVSCode插件scrcpy2️⃣ ADB方式实战控制微信启动正确设置autox选项启动服务执行代码停止代码执行3️⃣ 踩坑函数id需要传递全名称app.launch未生效ui.layout报错&#x1f4d6; 参考资料&#x1f6eb; 导读 需求…

基于区块链技术的信息服务新架构探讨

【摘 要】为探索区块链技术对信息通信基础架构的影响及实现,在梳理信息技术架构和信息服务架构演进的基础上,分析了区块链新型数字化分布式账本体系赋予数字世界生产关系升级的功能,并基于“云-管-端-边”信息服务架构研究了区块链技术实现信息物理空间中“设备民主”的具体…

EL表达式与JSTL标签库(JSP标准标签库)

EL表达式 EL 全名为Expression Language&#xff0c;是表达式语言。 EL表达式主要是代替jsp页面中的表达式脚本在jsp页面中进行数据的输出。因为EL表达式在输出数据的时候&#xff0c;要比jsp的表达式脚本要简洁很多。 不需要加任何jar包。不依赖任何其它库。提供了在脚本元素…

《软件开发本质论》笔记——了解价值,然后从可能去做的所有事情中选择那些最重要的去做

目录 一、传统的软件项目分阶段进行 二、根据”挑战性的目标“制订计划&#xff0c;危害性很大 三、推荐随时发现缺陷随时修复 四、 价值是什么 一、传统的软件项目分阶段进行 潜在风险&#xff1a;试图去计划并实现所有的功能特性&#xff0c;这使我们处于不利的境地。我们…

转行互联网,零基础应届生应该选择什么样的岗位作为切入点?

对于想要转行互联网的零基础小白&#xff0c;如果你耐心看完了这个答案&#xff0c;恭喜你已经找到了转行互联网的方法&#xff01;我不说空的理论&#xff0c;也不讲心灵鸡汤&#xff0c;作为文艺青年&#xff0c;我只说经验&#xff0c;我是工作十三年的互联网老兵&#xff0…