Chapter 04

第四章 何度も登場する同じ条件での分岐

kuramapommel
kuramapommel
2021.04.23に更新
このチャプターの目次

足りない分岐

前の章の終わりに、意味ありげに 「ちょっとだけ」 という言葉を添えましたが、実は enum にして条件をまとめただけでは、網羅性の担保の問題は解決したとは言えません

勘の鋭い方からは 確かに何通りの条件があるのかはすぐにわかるようになったけど、それらの条件を全て網羅して分岐しているしているかは保証できていないんじゃない? という疑問が投げられていそうです

まさにその通りで、例えば下記のコードはコンパイルが通ってしまいます

enum WEAPON_TYPE {
  LONG_SWORD,
  DUAL_BLADES,
  BOW
}

function attack(type: WEAPON_TYPE) {
  switch (type) {
    case WEAPON_TYPE.LONG_SWORD:
      console.log("太刀で攻撃")
      return

    case WEAPON_TYPE.DUAL_BLADES:
      console.log("双剣で攻撃")
      return

// WEAPON_TYPE.BOW での分岐が漏れてしまっている
//    case WEAPON_TYPE.BOW:
//      console.log("弓で攻撃")
//      return
  }
  
  return
}

// 分岐が漏れてしまっている WEAPON_TYPE.BOW を渡してしまっている
attack(WEAPON_TYPE.BOW)

言語によっては enum (や、それに類するもの)で分岐を行なった場合、すべての条件が網羅されていないとコンパイルエラーやワーニングといった形で教えてくれる言語もありますが、ほとんどの言語はそんな優しい仕様にはなっていないでしょう

また、先のコードのように enum が定義されている箇所と使われる箇所が同じファイル内など近い箇所に存在するのであれば、人の目で見て足りる足りないの判断はギリギリ可能(運用でカバーできる)かもしれませんが、 enum や定数などは定数定義用のファイルにまとめられることが往々にしてあります

複製される条件分岐

使われる場所が一箇所であればまだわかりやすいかもしれませんが、 「同じ条件分岐で異なる動きをさせたいとき」 も往々にしてあり、下記のコードのように同じ条件分岐が複数箇所で使われることになるでしょう

// enum 定義は省略

function attack(type: WEAPON_TYPE) {
  switch (type) {
    case WEAPON_TYPE.LONG_SWORD:
      console.log("太刀で攻撃")
      return

    case WEAPON_TYPE.DUAL_BLADES:
      console.log("双剣で攻撃")
      return

    case WEAPON_TYPE.BOW:
      console.log("弓で攻撃")
      return
  }
  
  return
}

function getName(type: WEAPON_TYPE): string {
  // attack と全く同じ分岐
  switch (type) {
    case WEAPON_TYPE.LONG_SWORD:
      // 振る舞いが異なる
      return "太刀"

    case WEAPON_TYPE.DUAL_BLADES:
      return "双剣"

    case WEAPON_TYPE.BOW:
      return "弓"
  }
  
  return
}

いよいよコードが複雑になってきましたね
この辺りから きな臭さ を感じ始めている方もいらっしゃるかと思います

このやり方で進めていくと以下のような問題が発生します

  • 仕様が増える度に 全く同じ条件での分岐を何箇所も書かないといけない
  • enum に新しい条件用の定数が追加された時に、 一部の分岐箇所に追加の対応漏れが発生する

こういった問題を防ぐためにはどうしたら良いでしょうか

同じ条件での分岐を一回にまとめること ができれば、ギリギリ運用でカバーできそうだったところまでは戻せそうです

次の章からは、どのようにして分岐を一回にまとめるのかを解説していきましょう