⚛️ 在 React 中应用策略模式(第一部分)

2025-05-25

⚛️ 在 React 中应用策略模式(第一部分)

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

让我们深入探讨问题细节及其解决方法。正如标题所示,我们将使用策略模式来解决它。

问题:散弹枪式手术

散弹枪手术是一种代码异味,任何修改都需要对许多不同的地方进行许多小的改动

散弹枪式代码异味
(图片来源: https: //refactoring.guru/smells/shotgun-surgery

这在项目中是如何实现的?假设我们需要为一款产品添加定价卡,并根据客户来源调整价格、货币、折扣策略和信息:

定价卡

在这个虚构的例子中,如果没有本地化,定价卡可能会按如下方式实现:

  • 成分:PricingCardPricingHeaderPricingBody
  • 实用功能:(在utils/discount.tsgetDiscountMessage),(在utils/price.ts中)。formatPriceByCurrency
  • PricingBody组件还计算最终价格。

以下是完整的实现:

现在假设我们需要更改某个国家/地区的定价方案,或者为另一个国家/地区添加新的定价方案。您需要如何处理上述实现?您至少需要修改 3 处代码,并在已经很混乱的if-else代码块中添加更多条件语句:

  • 修改PricingBody组件。
  • 修改getDiscountMessage功能。
  • 修改formatPriceByCurrency功能。

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

解决方案:策略模式

策略模式非常简单。我们可以简单地理解为,我们针对各个国家/地区的每个定价计划都是一个策略。在该策略类中,我们实现了该策略的所有相关逻辑。

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



import { Country, Currency } from '../../types';

abstract class PriceStrategy {
  protected country: Country = Country.AMERICA;
  protected currency: Currency = Currency.USD;
  protected discountRatio = 0;

  getCountry(): Country {
    return this.country;
  }

  formatPrice(price: number): string {
    return [this.currency, price.toLocaleString()].join('');
  }

  getDiscountAmount(price: number): number {
    return price * this.discountRatio;
  }

  getFinalPrice(price: number): number {
    return price - this.getDiscountAmount(price);
  }

  shouldDiscount(): boolean {
    return this.discountRatio > 0;
  }

  getDiscountMessage(price: number): string {
    const formattedDiscountAmount = this.formatPrice(
      this.getDiscountAmount(price)
    );

    return `It's lucky that you come from ${this.country}, because we're running a program that discounts the price by ${formattedDiscountAmount}.`;
  }
}

export default PriceStrategy;


Enter fullscreen mode Exit fullscreen mode

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



<PricingCard price={7669} strategy={new JapanPriceStrategy()} />


Enter fullscreen mode Exit fullscreen mode

的 propsPricingCard定义为:



interface PricingCardProps {
  price: number;
  strategy: PriceStrategy;
}


Enter fullscreen mode Exit fullscreen mode

再说一次,如果你了解 OOP,你就会知道我们不仅使用继承,而且还使用多态性。

以下是该解决方案的完整实现:

让我们再问一遍同样的问题:如何为新的国家/地区添加新的定价方案?有了这个解决方案,我们只需添加一个新的策略类,无需修改任何现有代码。这样,我们也满足了 SOLID 规范。

结论

因此,通过检测 React 代码库中的代码异味——散弹枪式手术(Shotgun Surgery),我们应用了一种设计模式——策略模式——来解决这个问题。我们的代码结构如下:

手术前 - 散弹枪式手术

对此:

之后 - 策略模式

现在我们的逻辑集中在一个地方,不再分散在各个地方。请注意,整篇文章都围绕着一个精心设计的示例展开。实际上,策略模式可以用更简单的方式实现(使用对象而不是类)。请查看本系列的第二部分:


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

文章来源:https://dev.to/itswillt/applying-design-patterns-in-react-strategy-pattern-enn
PREV
⚛️ 揭秘 React 的类型:组件类型
NEXT
一种不同的前端架构方法