ネストした条件分岐の迷宮
★★★「条件を満たすときだけ処理を進める」つもりで if を重ねると、本来やりたい処理がネストの最深部に沈む。どこからどこまでが各 if のブロックなのか目で追うのが難しくなり、条件を1つ足すたびにさらに深くなる。分岐構造を正確に理解しないまま仕様変更すれば、そのままバグになる。
01 確定申告の電子申告を送信できるかの判定
前提 確定申告データを電子送信できるかを判定し、可能なら送信処理を実行する
self.is_within_filing_period()self.all_forms_validatedself.certificate.is_valid()def submit_tax_return(self) -> None:
# 申告期間内か
if self.is_within_filing_period():
# 全帳票の検算が済んでいるか
if self.all_forms_validated:
# 電子証明書が有効か
if self.certificate.is_valid():
self.transmit()def submit_tax_return(self) -> None:
if not self.is_within_filing_period():
return
if not self.all_forms_validated:
return
if not self.certificate.is_valid():
return
self.transmit()第1章ではまず「これが読みにくさとバグの温床になる」と知覚するところまで。条件を反転して先に抜ける早期 return による解消は、第6章のトピック1で扱う。
02 else がどの if と対なのか追えなくなる
前提 確定申告の現在の状況を文字列で返す(送信可能 / どの条件が欠けているか)
self.is_within_filing_period()self.all_forms_validatedself.certificate.is_valid()def build_filing_status(self) -> str:
if self.is_within_filing_period():
if self.all_forms_validated:
if self.certificate.is_valid():
status = "送信可能"
else:
status = "電子証明書の更新が必要"
else:
status = "検算未了の帳票あり"
else:
status = "申告期間外"
return statusdef build_filing_status(self) -> str:
if not self.is_within_filing_period():
return "申告期間外"
if not self.all_forms_validated:
return "検算未了の帳票あり"
if not self.certificate.is_valid():
return "電子証明書の更新が必要"
return "送信可能"各ブロックの中身が数十行に育つと、対応する else は画面の遥か下に行ってしまう。この構造のまま条件を追加し続けたコードは実在し、読む者の時間と気力を奪う。
もう一段抽象化: ネストで潜る vs ガードで戻す
Bad と Good は判定している条件自体は同じ。違うのは読み手の進路。ネスト if は条件を満たすたびに右へ潜り、主処理が奥へ沈む。早期 return は進めない条件をその場で出口へ流し、最後に残った本処理だけを左端に置く。
読者は「今どの if の中か」を覚えながら右へ潜る。
読者は「ここで終わる理由」を上から消していくだけでよい。
定義として見ると: 同じ集合を別方向から指している
ネスト if
A ∩ B ∩ C成功条件の交差を内側へ組み立てる。
早期 return
U \ (¬A ∪ ¬B ∪ ¬C)失敗条件を外へ返し、残った集合を処理する。
| Bad: ネストで潜る | Good: ガードで戻す | |
|---|---|---|
| 書き方 | A → B → C と条件を満たすたびに内側へ進む | not A / not B / not C を先に返し、残りを処理する |
| 読者の視線 | 右へ右へとインデントを追う | 上から順に「ここで終わる理由」を読む |
| 主処理の位置 | ネストの最深部に沈む | 左端、関数の末尾に並ぶ |
| 条件追加 | さらに1段深くなる | 出口を1行足すだけ |
この「先に終わる理由を並べ、残りを本処理にする」発想は、確定申告に限らず多くの場面で現れる。
- 型の絞り込み
if not isinstance(x, str): return # 違う型を切る → 残りは str - バリデーション
if amount <= 0: raise ValueError # 不正値を弾く → 残りは安全な値 - フィルタパイプライン
valid_items = [i for i in items if not i.is_invalid] # 不正を除外 → 残りを処理 - 権限チェック
if not user.is_admin: return 403 # 権限なしを切る → 残りは admin
ネストを読む作業は「どこまで潜ったか」を覚える作業になる。ガード節は「ここで終わる理由」を先に片づけるので、読み手は最後に残った本処理だけに集中できる。