はじめに

この記事は、Javaで業務ロジックを書いていて「ifがネストして読めない!」と悩んでいる中級者のエンジニアを対象にしています。
サンプルコードを通して「Guard節(ガード節)」と「Early Return(早期リターン)」を使ったリファクタリングの基本をお伝えします。読み終えると、即戦用のテクニック+αでチームメンバーに差をつけられるレベルのコードが書けるようになります。

前提知識

  • Javaの基本的な文法(if/else、メソッド定義)
  • IntelliJ IDEAなどのIDEでデバッグ実行ができること
  • 読みづらいコードに対して「何とかしたい」と思っていること

なぜネストが深くなるのか? & なぜ削るべきなのか

Javaはビジネス用途で幅広く使われ、仕様変更の連続で条件分岐が増えがちです。
「条件Aかつ条件Bかつ条件Cのときだけ本処理」といったパターンを素直にifで書くと、右にスライドするようにインデントが深くなります。

ネストが深くなると以下のデメリットが出ます。 - 可読性が落ちる → 後続の改修でバグを埋め込みやすい - 単体テストの分岐数が指数的に増える → テスト工数が爆発 - コードレビューで指摘されがち → 出荷まで時間がかかる

Guard節とEarly Returnを使えば、「異常系を早めにさばいて本処理に集中」 することで、ネストを減らし読みやすいコードに変身させられます。

Guard節+Early Returnで「ガチっと」リファクタリング

ここでは、受注管理システムの「注文承認」ロジックを題材に、段階的にリファクタリングしていきます。

ステップ1:典型的な深いネストコード

まず、以下のようなコードがあったとします。

Java
public 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節 は「条件に合致しない(=異常)なら即座にリターン/例外を投げる」スタイルです。
上からチェックして、早めに「異常ならこれ以上処理しない」を明示します。

Java
public 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済 です。
もし「正常系の分岐が複数」ある場合は、以下のように先にリターンさせるとネストを減らせます。

Java
public 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を返して「成功/失敗+理由」をまとめて返すと保守性が高まります。

Java
public 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を排除
  • 読みやすく、テストしやすく、バグを埋めにくいコードに生まれ変わる

このテクニックはどんなビジネスロジックにも適用可能です。
次回は「複雑な条件をドメインオブジェクトに閉じ込める」リファクタリングを紹介する予定です。

参考資料