複雑な if-else、フロントエンド戦略パターンの実践を拒否する

デザインパターンの重要性

なぜデザインパターンを学び、使用するのか、主な理由は 2 つあると思います。

  1. デカップリング: デザインパターンの目的は、「変更されていないもの」を「変数」から分離し、「変更されていないもの」を統一されたオブジェクトにカプセル化して、具体的な例で「変数」を実装することです。
  2. 統一標準を定義する: 優れたコードの標準セットを定義します。優れたコードを実装するためのマニュアルに相当します。

フロントエンドの開発プロセスでは、複雑なシナリオに直面してもコードロジックをより明確に扱うことができます。その中でも、ストラテジモードはフロントエンドの作業でよく使用されます。ストラテジの具体的な適用についてお話します。フロントエンド開発のモード。

戦略パターンの基本

戦略パターンの意味は、一連のアルゴリズムが定義され、各アルゴリズムが相互に置き換えられるようにカプセル化されているということです。

戦略パターンについての私の個人的な理解は、分離の目的を達成するために、もともと 1 つの関数で記述されていた関数セット全体を別々の部分に分割することです。したがって、戦略パターンの最適な適用シナリオは、if-else を逆アセンブルし、各 if モジュールを独立したアルゴリズムとしてカプセル化することです。

オブジェクト指向言語では、戦略パターンは通常 3 つの部分から構成されます。

  • 戦略 (Strategy): さまざまなアルゴリズムのインターフェイスを実装する
  • 具体的な戦略: 戦略によって定義されたインターフェイスを実装し、特定のアルゴリズム実装を提供します。
  • Context: ストラテジー オブジェクトへの参照を保持し、それを ConcreteStrategy オブジェクトで構成し、Strategy オブジェクトへの参照を維持します。

このように定義を見るのは直感的ではないかもしれませんが、ここでは TS オブジェクト指向の方法で実装された計算機戦略パターンの例を使用して説明します。

// 第一步: 定义策略(Strategy)
interface CalculatorStrategy {
  calculate(a: number, b: number): number;
}

// 第二步:定义具体策略(Concrete Strategy)
class AddStrategy implements CalculatorStrategy {
  calculate(a: number, b: number): number {
    return a + b;
  }
}

class SubtractStrategy implements CalculatorStrategy {
  calculate(a: number, b: number): number {
    return a - b;
  }
}

class MultiplyStrategy implements CalculatorStrategy {
  calculate(a: number, b: number): number {
    return a * b;
  }
}

// 第三步: 创建上下文(Context),用于调用不同的策略
class CalculatorContext {
  private strategy: CalculatorStrategy;

  constructor(strategy: CalculatorStrategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy: CalculatorStrategy) {
    this.strategy = strategy;
  }

  calculate(a: number, b: number): number {
    return this.strategy.calculate(a, b);
  }
}

// 使用策略模式进行计算
const addStrategy = new AddStrategy();
const subtractStrategy = new SubtractStrategy();
const multiplyStrategy = new MultiplyStrategy();

const calculator = new CalculatorContext(addStrategy);
console.log(calculator.calculate(5, 3)); // 输出 8

calculator.setStrategy(subtractStrategy);
console.log(calculator.calculate(5, 3)); // 输出 2

calculator.setStrategy(multiplyStrategy);
console.log(calculator.calculate(5, 3)); // 输出 15

フロントエンド戦略パターンの適用

実際、フロントエンド開発では通常、オブジェクト指向パターンは使用されず、フロントエンドでの戦略パターンの適用は 2 つの部分に簡略化できます。

  1. オブジェクト: ストレージ ポリシー アルゴリズム。対応するアルゴリズムをキーで照合します。
  2. 戦略メソッド: キーに対応する特定の戦略アルゴリズムを実装します。

最近の開発プロセスでストラテジモードリファクタリングを適用した例です 実現された機能は、異なる操作の関連フィールドの連携に対処することです 元のコードでは、操作の種類に多くの if-else 判定が使用されていますopType. コードはおそらく次のようになります はい、比較的小さく見えますが、各 if に多くの処理ロジックがある場合、全体の可読性は非常に低くなります

export function transferAction() {
  actions.forEach((action) => {
    const { opType } = action

    // 展示 / 隐藏字段
    if (opType === OP_TYPE_KV.SHOW) { }
    else if (opType === OP_TYPE_KV.HIDE) {}
    // 启用 / 禁用字段
    else if (opType === OP_TYPE_KV.ENABLE) { }
    else if (opType === OP_TYPE_KV.DISABLE) {}
    // 必填 / 非必填字段
    else if (opType === OP_TYPE_KV.REQUIRED) { }
    else if ((opType === OP_TYPE_KV.UN_REQUIRED) { }
    //  清空字段值
    else if (opType === OP_TYPE_KV.CLEAR && isSatify) { }
  })
}

在使用策略模式重构之后,将每个 action 封装进单独的方法,再把所用的算法放入一个对象,通过触发条件匹配。这样经过重构后的代码,相比于原来的 if-else 结构更清晰,每次只要找到对应的策略方法实现即可。并且如果后续有扩展,只要继续新的增加策略方法就好,不会影响到老的代码

export function transferAction( /* 参数 */ ) {
  /**
   * @description 处理字段显示和隐藏
   */
  const handleShowAndHide = ({ opType, relativeGroupCode, relativeCode }) => {}

  /**
   * @description // 启用、禁用字段(支持表格行字段的联动)
   */
  const handleEnableAndDisable = ({ opType, relativeGroupCode, relativeCode }) => {}

  /**
   * @description 必填 / 非必填字段(支持表格行字段的联动)
   */
  const handleRequiredAndUnrequired = ({ opType, relativeGroupCode, relativeCode }) => {}

  /**
   * @description 清空字段值
   */
  const handleClear = ({ opType, relativeGroupCode, relativeCode }) => {}

  // 联动策略
  const strategyMap = {
    // 显示、隐藏
    [OP_TYPE_KV.SHOW]: handleShowAndHide,
    [OP_TYPE_KV.HIDE]: handleShowAndHide,
    // 禁用、启用
    [OP_TYPE_KV.ENABLE]: handleEnableAndDisable,
    [OP_TYPE_KV.DISABLE]: handleEnableAndDisable,
    // 必填、非必填
    [OP_TYPE_KV.REQUIRED]: handleRequiredAndUnrequired,
    [OP_TYPE_KV.UN_REQUIRED]: handleRequiredAndUnrequired,
    // 清空字段值
    [OP_TYPE_KV.CLEAR]: handleClear,
  }

  // 遍历执行联动策略
  actions.forEach((action) => {
    const { opType, relativeGroupCode, relativeCode, value } = action

    if (strategyMap[opType]) {
      strategyMap[opType]({ /* 入参 */ })
    }
  })
}

总结

策略模式的优点在于:代码逻辑更清晰,每个策略对对应一个实现方法;同时遵循开闭原则,新的策略方法无需改变已有代码,所以非常适合处理或重构复杂逻辑的 if-else

在前端开发过程中,不需要遵循面向对象的应用方式,只需要通过对象存储策略算法,通过 key 匹配具体策略实现,就可以实现一个基础的策略模式

おすすめ

転載: juejin.im/post/7256721204300202042