深すぎるネストはバグの温床!ガード節でシンプルにする条件分岐の書き方

この記事は、読みやすいコードを書くための実践ガイドシリーズの一つです。前回の記事「その名前、未来の自分を苦しめますよ?読みやすいコードを書くための命名規則入門」も併せてお読みいただくと、より理解が深まります。

こんにちは。今回は、コードの可読性を損なうもう一つの大きな要因、「条件分岐のネスト」に焦点を当てます。

if文が何重にも重なったコードは、まるで迷路のようです。どこを通れば正常な処理にたどり着くのか、一目で理解するのは非常に困難です。このようなコードが、いかにしてバグの温床となるのか、そしてどうすれば改善できるのかを見ていきましょう。

目次

なぜ、深いネストは悪いのか?

if文が入れ子になるほど、私たちの脳が一度に処理しなければならない条件の組み合わせは指数関数的に増えていきます。

【Before】改善前のコード

// 魔法を詠唱するロジック
public void castSpell(PlayerCharacter character, Spell spell) {
    // ネスト レベル 1
    if (character.getHitPoint() > 0) {
        // ネスト レベル 2
        if (character.canMove()) {
            // ネスト レベル 3
            if (spell.getRequiredMana() <= character.getManaPoint()) {
                character.consumeMana(spell.getRequiredMana());
                character.chant(spell);
            }
        }
    }
}

このコードのメインの処理は、一番内側にあるchant(spell)の部分です。しかし、そこにたどり着くまでに3つの条件をクリアしなければなりません。もし、ここにelseが加わったらどうなるでしょうか?コードの複雑さは一気に増し、仕様の変更やデバッグが非常に困難になります。

このような深いネストは、以下のような問題を引き起こします。

  • 可読性の低下: 正常系の処理がどこにあるのか分かりにくい。
  • 修正の困難さ: 新しい条件を追加する際に、どこに挿入すれば良いか迷う。
  • バグの混入: 条件の組み合わせを網羅したテストが難しく、考慮漏れが発生しやすい。

改善策:ガード節でネストを解消する

この問題を解決する効果的なテクニックが「ガード節(Guard Clauses)」です。これは、関数の冒頭でエラー条件や事前条件をチェックし、満たさない場合はすぐに処理を中断(return)するという手法です。

【After】改善後のコード 先ほどのコードをガード節を使ってリファクタリングしてみましょう。

// 魔法を詠唱するロジック
public void castSpell(PlayerCharacter character, Spell spell) {
    // ガード節1: HPが0以下なら、何もせず終了
    if (character.getHitPoint() <= 0) {
        return;
    }
    // ガード節2: 行動不能なら、何もせず終了
    if (!character.canMove()) {
        return;
    }
    // ガード節3: マナが足りないなら、何もせず終了
    if (spell.getRequiredMana() > character.getManaPoint()) {
        return;
    }

    // 全てのチェックをクリアした場合のメイン処理
    // インデントが深くなっていないことに注目!
    character.consumeMana(spell.getRequiredMana());
    character.chant(spell);
}

いかがでしょうか。ネストがなくなり、上から下へまっすぐに読めるコードになりました。異常系の処理は関数の早い段階で除外され、その後に続くのがメインの正常系ロジックであることが一目瞭然です。

巨大なネストには「メソッドの抽出」も有効

もし、条件分岐自体が非常に複雑で長大な場合は、条件判定のロジックを別のメソッドとして切り出す「メソッドの抽出(Extract Method)」も有効です。

public void complexProcess(User user, Order order) {
    // 巨大なネストの代わりに...
    if (!isProcessable(user, order)) {
        return;
    }

    // メインの処理
    // ...
}

// 条件判定ロジックを別のメソッドにまとめる
private boolean isProcessable(User user, Order order) {
    if (!user.isActivated()) return false;
    if (user.isSuspended()) return false;
    if (!order.hasStock()) return false;
    // ...その他の複雑な条件
    return true;
}

このようにロジックを分離することで、complexProcessメソッドは「何をするか」に集中でき、isProcessableメソッドは「どのように判定するか」に集中できます。これにより、それぞれの関心事が分離され、コード全体の理解が容易になります。

まとめ

条件分岐の深いネストは、コードの複雑さを増大させ、バグの温床となります。「if文のネストを見たら、まずガード節を検討する」という習慣を身につけるだけで、あなたのコードは劇的にシンプルで堅牢になるはずです。

▼次の記事 次回は「凝集度の高いクラス設計」をテーマに、データとロジックの適切な関係性について解説します。

データと振る舞いを分離させない!凝集度の高いクラス設計のすすめ

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次