はじめに
この記事は、Javaで業務ロジックを書いていて「ifがネストして読めない!」と悩んでいる中級者のエンジニアを対象にしています。
サンプルコードを通して「Guard節(ガード節)」と「Early Return(早期リターン)」を使ったリファクタリングの基本をお伝えします。読み終えると、即戦用のテクニック+αでチームメンバーに差をつけられるレベルのコードが書けるようになります。
前提知識
- Javaの基本的な文法(if/else、メソッド定義)
- IntelliJ IDEAなどのIDEでデバッグ実行ができること
- 読みづらいコードに対して「何とかしたい」と思っていること
なぜネストが深くなるのか? & なぜ削るべきなのか
Javaはビジネス用途で幅広く使われ、仕様変更の連続で条件分岐が増えがちです。
「条件Aかつ条件Bかつ条件Cのときだけ本処理」といったパターンを素直にifで書くと、右にスライドするようにインデントが深くなります。
ネストが深くなると以下のデメリットが出ます。 - 可読性が落ちる → 後続の改修でバグを埋め込みやすい - 単体テストの分岐数が指数的に増える → テスト工数が爆発 - コードレビューで指摘されがち → 出荷まで時間がかかる
Guard節とEarly Returnを使えば、「異常系を早めにさばいて本処理に集中」 することで、ネストを減らし読みやすいコードに変身させられます。
Guard節+Early Returnで「ガチっと」リファクタリング
ここでは、受注管理システムの「注文承認」ロジックを題材に、段階的にリファクタリングしていきます。
ステップ1:典型的な深いネストコード
まず、以下のようなコードがあったとします。
Javapublic class OrderService { public void approveOrder(Order order, User user, LocalDateTime now) { if (order != null) { if (user != null) { if (order.getStatus() == OrderStatus.PENDING) { if (user.hasPermission("APPROVE_ORDER")) { if (!order.isExpired(now)) { // 本処理:承認 order.setStatus(OrderStatus.APPROVED); order.setApprovedBy(user); order.setApprovedAt(now); repository.save(order); notifier.sendApprovedMail(order); } else { throw new BusinessException("注文の有効期限が切れています"); } } else { throw new BusinessException("承認権限がありません"); } } else { throw new BusinessException("ステータスが承認待ちではありません"); } } else { throw new BusinessException("ユーザーがnullです"); } } else { throw new BusinessException("注文がnullです"); } } }
インデントが4段もあって、本処理が右端に追いやられています。
ここからGuard節とEarly Returnを使って削っていきます。
ステップ2:Guard節で異常系を先に処理する
Guard節 は「条件に合致しない(=異常)なら即座にリターン/例外を投げる」スタイルです。
上からチェックして、早めに「異常ならこれ以上処理しない」を明示します。
Javapublic void approveOrder(Order order, User user, LocalDateTime now) { // Guard節 if (order == null) throw new BusinessException("注文がnullです"); if (user == null) throw new BusinessException("ユーザーがnullです"); if (order.getStatus() != OrderStatus.PENDING) throw new BusinessException("ステータスが承認待ちではありません"); if (!user.hasPermission("APPROVE_ORDER")) throw new BusinessException("承認権限がありません"); if (order.isExpired(now)) throw new BusinessException("注文の有効期限が切れています"); // 本処理 order.setStatus(OrderStatus.APPROVED); order.setApprovedBy(user); order.setApprovedAt(now); repository.save(order); notifier.sendApprovedMail(order); }
見ての通り、インデントが一段になり、「本処理に集中できるゾーン」 が一目瞭然です。
加えて、各Guard節の条件式は「否定形」で書くことで「何がダメなのか」を直感的に示せます。
ステップ3:Early Returnでさらに読みやすく
今回はすでに例外を投げてリターンしているため、事実上Early Return済 です。
もし「正常系の分岐が複数」ある場合は、以下のように先にリターンさせるとネストを減らせます。
Javapublic String buildMessage(User user) { if (user == null) return "ゲストさん"; if (!user.isActivated()) return "本登録が完了していません"; return user.getName() + "さん、こんにちは!"; }
ハマった点とエラー解決
1. Guard節の条件が逆!
「条件に合致したらリターン」ではなく「不適合ならリターン」を書かないと、意図した動作になりません。
if (order == null) return; としていきなりreturnしてしまうと、呼び出し元が「何で失敗したのか」分からなくなるので、例外または専用のResultオブジェクト を返すようにしましょう。
2. 複数の戻り値を扱うとき
例外を使いたくないケースでは、Result型やOptionalを返して「成功/失敗+理由」をまとめて返すと保守性が高まります。
Javapublic Result approveOrder(Order order, User user, LocalDateTime now) { if (order == null) return Result.error("注文がnullです"); ... return Result.ok(order); }
まとめ
本記事では、Javaのifネストを削る「Guard節」と「Early Return」の基本を解説しました。
- Guard節で「異常系を先にさばく」ことでインデントを一段に
- Early Returnで「条件クリア後は即座にリターン」し、不要なelseを排除
- 読みやすく、テストしやすく、バグを埋めにくいコードに生まれ変わる
このテクニックはどんなビジネスロジックにも適用可能です。
次回は「複雑な条件をドメインオブジェクトに閉じ込める」リファクタリングを紹介する予定です。
参考資料
- マーティン・ファウラー『リファクタリング(第2版)』オーム社
- Java公式ドキュメント -例外とアサーション
- IntelliJ IDEA リファクタリングガイド
