ガード節より読みやすい!?途中リターンせずにネストを浅くする「ガードレール構文」の紹介

プログラムで値のチェック処理を書くとき、普通にif文を使っているとネストが深くなっていってしまいます。

function alertOverweight(user) {
  let result, height, weight;

  // 身長と体重が数値であることを確認
  if (!(isNaN(user.height) || isNaN(user.weight))) {
    height = user.height / 100; // 身長を取得
    weight = user.weight; // 体重を取得

    // 身長が0より大きいことを確認
    if (height > 0) {
      user.bmi = weight / height ** 2; // BMIを計算

      // BMIが25以上であることを確認
      if (user.bmi >= 25) {
        alert('体重を減らしましょう。');

        result = '過体重でした。';
      } else {
        result = 'BMIが25未満のため、過体重ではありませんでした。';
      }
    } else {
      result = '身長が0以下のため、BMIが計算できませんでした。';
    }
  } else {
    result = '身長もしくは体重が数値でないため、BMIが計算できませんでした。';
  }
  return result;
}

このようにネストが深くなると、コードの可読性が低下してしまいます。

ガード節のデメリット

ガード節は、このようなネストを解消するための手法として広く使われています。

function alertOverweight(user) {
  let height, weight;

  // 身長もしくは体重が数値でない場合は結果を返す
  if (isNaN(user.height) || isNaN(user.weight)) {
    return '身長もしくは体重が数値でないため、BMIが計算できませんでした。';
  }
  height = user.height / 100; // 身長を取得
  weight = user.weight; // 体重を取得

  // 身長が0以下の場合は結果を返す
  if (height <= 0) {
    return '身長が0以下のため、BMIが計算できませんでした。';
  }
  user.bmi = weight / height ** 2; // BMIを計算

  // BMIが25未満の場合は結果を返す
  if (user.bmi < 25) {
    return 'BMIが25未満のため、過体重ではありませんでした。';
  }
  alert('体重を減らしましょう。');

  return '過体重でした。';
}

このように途中リターンを使って、チェック処理で関数から抜け出すことでネストを無くしているのです。これは一見するとコードがフラットになりますが、構造化プログラミングの観点ではあまり好ましくありません。例えば、この関数は最終的に「過体重でした。」という結果を返していますが、様々な条件を満たした結果であることが分かり難いといった問題が生じます。

途中リターンしなくてもネストは浅くできる

この問題を解決するために生まれたのが、今回紹介するガードレール構文です。先ほどのコードを、ガードレール構文を使って書き直すと以下のようになります。

function alertOverweight(user) {
  let result, proceed, height, weight;
  
  result = '身長もしくは体重が数値でないため、BMIが計算できませんでした。';
  proceed = !(isNaN(user.height) || isNaN(user.weight)); // 身長と体重が数値であることを確認
  if (proceed) {
    height = user.height / 100; // 身長を取得
    weight = user.weight; // 体重を取得

    result = '身長が0以下のため、BMIが計算できませんでした。';
    proceed = height > 0; // 身長が0より大きいことを確認
  } if (proceed) {
    user.bmi = weight / height ** 2; // BMIを計算

    result = 'BMIが25未満のため、過体重ではありませんでした。';
    proceed = user.bmi >= 25; // BMIが25以上であることを確認
  } if (proceed) {
    alert('体重を減らしましょう。');

    result = '過体重でした。';
  }
  return result;
}

このコードでは、result変数とproceed変数を段階的に更新することで、チェック処理を進めていきます。一度条件を外れると、それ以降のif文はすべてスルーして、最後に設定された結果を返す仕組みです。

if文が処理の流れに沿って直線状に連なっている様子から、これを「ガードレール構文」と呼ぶことにしました。

これならネストを減らしつつ、2つの変数から一連のチェック処理を芋づる式に辿っていくこともできるため、コードの見通しがとても良くなります。

まとめ

途中リターンは特定の状況下(ツリー構造の処理をする再帰関数等)で有効な場合もありますが、通常の制御構造(if文やfor文等)で実装できる場合は、極力使わないようにしましょう。

以上、途中リターンせずにネストを浅くする「ガードレール構文」の紹介でした。是非皆さんもガード節の代わりに使ってみてください。

参考記事

https://memorandumrail.com/program-conditional-branch

https://marycore.jp/coding/why-goto-statement-is-bad

https://www.ninton.co.jp/archives/8065

関連記事

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です