在 React 前端開發時,常常會需要在不同的元件、hook、utils 中寫一些邏輯。

有些時候,使用策略模式會很有幫助,這篇文章給您參考。

  • 原文出處:https://dev.to/itshugo/applying-design-patterns-in-react-strategy-pattern-enn

出問題了:霰彈槍手術(Shotgun Surgery)

Shotgun Surgery 是一種程式寫很爛的信號。想對程式規格做一點小修改,需要改一大堆地方。

在專案中通常如何發生?假設我們需要為產品寫一個報價卡片,我們根據客戶所在國家,調整價格、貨幣、折扣方式和文字說明:

大多數工程師可能會這樣寫:

  • 元件:PricingCardPricingHeaderPricingBody

  • Utility functions:getDiscountMessage(在 utils/discount.ts 中),formatPriceByCurrency(在 utils/price.ts 中)。

  • PricingBody 元件會計算最終價格。

完整程式碼範例:https://codesandbox.io/s/react-strategy-pattern-problem-h59r02?from-embed

現在假設我們需要更改一個國家/地區的定價計劃,或為另一個國家/地區添加新的定價計劃。您將如何處理這段修改?您必須至少修改 3 個地方,並向已經滿亂的 if-else 區塊添加更多條件:

  • 修改 PricingBody 組件。

  • 修改 getDiscountMessage 函數。

  • 修改 formatPriceByCurrency 函數。

如果您聽說過 S.O.L.I.D 原則,我們已經違反了前 2 條原則:單一職責原則和開閉原則。

解決方法:策略模式

策略模式非常簡單。我們可以簡單的理解為,每個國家的定價方案都是一個策略。在那個策略類別中,會實現該策略的所有相關邏輯。

假設您熟悉 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;

我們只需將實例化的策略作為 prop 傳遞給 PricingCard 元件:

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

PricingCard 的 props 定義為:

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

同樣,如果您了解 OOP,那麼我們不僅在使用繼承,而且還在此處使用多態性(Polymorphism)。

完整程式碼範例:https://codesandbox.io/s/react-strategy-pattern-solution-mm0cvm?from-embed

使用這個解決方案,我們只需要添加一個新的策略類別,而不需要修改任何現有程式碼。這樣,我們也滿足了 S.O.L.I.D 原則。

結論

因為我們在 React 程式碼中檢測到程式碼發臭:Shotgun Surgery,所以我們使用了策略模式來解決它。

Before

After

現在邏輯都存在一個地方,不再分佈在許多地方。


以上是 Strategy Pattern 的簡單說明,希望對您有幫助。


共有 0 則留言