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

2023-10-30

这篇文章是关于我们许多人在 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,我们应用了一种设计模式——策略模式——来解决它。我们的代码结构来自于:

对此:

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 React 中应用设计模式:策略模式 的相关文章

随机推荐

  • SpringCloud之Eureka的报错(版本神坑)

    一 报错内容 2021 09 12 14 47 53 594 INFO 20640 freshExecutor 0 com netflix discovery DiscoveryClient Disable delta property f
  • springboot如何实现短信验证注册和短信验证码登录

    Spring Boot实现短信验证注册和短信验证码登录的步骤如下 1 集成短信服务 选择一个短信服务商 例如阿里云 腾讯云等 并集成该服务商提供的API 2 实现短信发送接口 编写一个短信发送的接口 该接口需要传入手机号并发送短信验证码到该
  • C#Socket通信基础方法知识整理

    一 IP地址操作类 1 IPAddress类 a 在该类中有一个 Parse 方法 可以把点分的十进制IP表示转化成IPAddress类 方法如下 IPAddress address IPAddress Parse 192 168 0 1
  • python sqlite3

    含数据库连接 表创建 增删改查 查看sqlite数据库的软件推荐使用sqlitestudio 下载地址 sqlitestudio SQLite文档类资源 CSDN下载 coding utf 8 乐乐感知学堂公众号 author https
  • SQL Server如何备份数据库

    一 首先把当前的数据库备份成一个文件 1 按照操作来 选择对应的数据库 确定备份文件的存储位置 点击确定 生成备份文件 2 然后可以通过该备份文件还原数据库 右键数据库点击还原文件和文件组 然后设置目标数据库的名字 如果数据库中已经存在相同
  • TSINGSEE青犀视频安防监控管理平台EasyNVR如何配置鉴权?

    视频监控汇聚平台EasyNVR是基于RTSP Onvif协议的视频平台 可支持将接入的视频流进行全平台 全终端的分发 分发的视频流包括RTSP RTMP HTTP FLV WS FLV HLS WebRTC等格式 为了满足用户的集成与二次开
  • Qt 串口类QSerialPort 使用笔记

    Qt 串口类QSerialPort 使用笔记 虽然现在大多数的家用PC机上已经不提供RS232接口了 但是由于RS232串口操作简单 通讯可靠 在工业领域中仍然有大量的应用 Qt以前的版本中 没有提供官方的对RS232串口的支持 编写串口程
  • virtual box安装Ubuntu操作系统

    在提供Ubuntu 18 10 Cosmic Cuttlefish映像的地址中有ubuntu 18 10 desktop amd64 iso和ubuntu 18 10 live server amd64 iso版本 它们是什么区别 简单的说
  • 机器学习——所有非支持向量的拉格朗日乘子一定为0

    问 SVM模型求解过程中所有非支持向量的拉格朗日乘子一定为0 答 正确 SVM模型的求解过程中 对于非支持向量的数据点 其对应的拉格朗日乘子为0 这是因为非支持向量数据点已经满足了约束条件 不需要对目标函数造成日对目标函数有贡献 简而言之
  • UDIMM、RDIMM和LRDIMM

    UDIMM RDIMM和LRDIMM UDIMM UDIMM 全称Unbuffered DIMM 即无缓冲双列直插内存模块 指地址和控制信号不经缓冲器 无需做任何时序调整 直接到达DIMM上的DRAM芯片 UDIMM由于在CPU和内存之间没
  • 基于python的Page Factory模式

    Pythium 基于 Python 的 Page Factory 设计模式测试库 类似于Java的Page Factory模式 旨在减少代码冗余 简单易用 具有高度的可扩展能力 支持以 annotation的方式定义元素 支持同一个元素多种
  • 【Unity 3D学习笔记】P&D 过河游戏智能实现

    P D 过河游戏智能帮助实现 实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 对于过河游戏 首先需要知道其中各个状态之间的转换关系 绘制状态转移图如下 其中 P代表出发岸上的牧师 D代表出发岸上的恶魔 加号和减
  • 竞品分析该怎么做

    竞品分析 作用 知己知彼 百战不殆 为自身产品设计提供功能 可用性 关键技术等方面的参考 提高自身产品的差异化程度 为新立项的产品 拍脑袋想出来的 降低风险 如何选择竞品 行业内领先的产品 通常可以根据一些百度指数 行业排名 业务相似程度来
  • 四款Python在线模拟器

    一 菜鸟工具 地址 http c runoob com compile 9 打开的界面是酱紫的 左边是代码输入框 右边是结果输出框 特点 1 支持切换Python2 Python3版本 2 不支持常用导入模块 例如pandas等 3 运行速
  • 使用Python生成docx文档

    1 首先需要安装doxc的公共库 pip install python docx U 2 安装成功后 使用这个库的方法import docx 3 这样生成的docx内容会有汉字显示不出来 4 这样生成的docx会有乱码 需要调整字体格式添加
  • 解决linux磁盘空间不足的方法

    磁盘空间不足的解决办法 1 首先确定是否是磁盘空间不足 输入命令 df h 查看磁盘信息 很明显 Filesystem下的挂载点 dev vda1 下的50G容量已经耗尽 这时最简单的办法就是找到大且无用的文件并删除 首选就是log文件 2
  • Flutter 常见问题总结

    文章目录 1 内容简介 2 使用Column等容器包裹ListView报错的问题 3 Navigator operation requested does not include a Navigator 4 设置Container背景色 5
  • Java开发中使用sql简化开发

    引语 在Java开发中 我们更希望数据库能直接给我们必要的数据 然后在业务层面直接进行使用 所以写一个简单的sql语句有助于提高Java开发效率 本文由简单到复杂的小白吸收 还请多多指教 使用MySQL数据库 先创建一个简单的表 DROP
  • elemenui自己本地跑起存在的问题&做自定义组件迭代规范

    npm install安装依赖出现PhantomJS not found on PATH 问题 PhantomJS not found on PATH PhantomJS not found on PATH Downloading http
  • 在 React 中应用设计模式:策略模式

    这篇文章是关于我们许多人在 React 和前端开发中遇到的一个问题 有时甚至没有意识到这是一个问题 在不同的组件 钩子 实用程序等中实现了一段逻辑 让我们深入了解问题的详细信息以及如何解决它 正如标题所暗示的 我们将使用策略模式来解决它 问