if文で空ブロックを実装する人の気持ちを考えてみる
if文のtrueブロックが空で、elseにのみ処理が書かれているコードが存在する理由を考察。コーディング規約・業務ルールの強調・設計書との対応維持など、空ブロックが書かれる7つのパターンを解説します。
プログラミングで if 文といえば、どの言語にもある基本的な構文ですね。
真偽値によって処理を分岐するために使われますが、たまにこんなコードを見かけることがあります。
if (hoge) {
} else {
// 何かしらの処理
}
真の場合の処理が空です。
初めて見ると、
Why? なぜ?
と思うかもしれません。
私自身も見るたびに、
- 条件を反転した方がよくない?
- ガード節にした方が読みやすくない?
- リファクタリング漏れでは?
と考えてしまいます。
今回は、なぜこのようなコードが書かれるのかを考えてみました。
空ブロックとは
本記事の文脈においては、真の処理が存在せず、ブロックの中が空になっている状態を指します。
if (hoge) {
} else {
doSomething();
}
一般的には次のように書き換えたくなります。
if (!hoge) {
doSomething();
}
あるいは早期リターンを使う場合。
if (hoge) {
return;
}
doSomething();
そのため、空ブロックを見ると少し気持ち悪く感じる人も多いのではないでしょうか。
どんな場合に空ブロックを書くのか
1. ! の使用がコーディング規約で禁止されている
昔見たコーディング規約に、
条件式で
!を使用しない
というルールがありました。
理由は、
- 見落としやすい
- 読み間違いを防ぎたい
- 記述スタイルを統一したい
といったものでした。
このような環境では、
if (!isValid) {
execute();
}
よりも、
if (isValid) {
} else {
execute();
}
の方が規約に沿った書き方になります。
個人的には少し違和感がありますが、組織としての判断なら理解できます。
2. 条件そのものを強調したい
例えば次のコード。
if (isAdmin) {
} else {
checkPermission();
}
このコードが伝えたいことは、
管理者は権限チェック不要
という業務ルールかもしれません。
条件反転すると、
if (!isAdmin) {
checkPermission();
}
になります。
意味は同じですが、書いた本人としては
管理者なら何もしない
というルールを強調したかった可能性があります。
3. 将来的に処理を追加する予定がある
開発途中のコードではよくあります。
if (newFeatureEnabled) {
// TODO: 新機能実装予定
} else {
legacyProcess();
}
今は処理がないけれど、将来的に実装する予定があるケースです。
ただし TODO コメントがない場合は意図が伝わりにくく、後から読んだ人は困るかもしれません。
4. 条件分岐の対称性を維持したい
複数の条件分岐が続く場合です。
if (status === "SUCCESS") {
} else if (status === "WARNING") {
notify();
} else {
throwError();
}
正常系を明示的に書くために空ブロックを置くケースがあります。
実際にはコメントがあった方が分かりやすいでしょう。
if (status === "SUCCESS") {
// 正常時は何もしない
} else if (status === "WARNING") {
notify();
} else {
throwError();
}
5. 元々処理があったが削除された名残
個人的にはこれが一番多い気がします。
最初はこうだったコード。
if (hoge) {
sendLog();
} else {
execute();
}
後からログ出力が不要になり、
if (hoge) {
} else {
execute();
}
となったものの、条件反転までは行われなかった。
長く運用されているシステムでは意外と見かけます。
6. 「何もしない」が仕様として重要
if (user.isGuest()) {
// ゲストユーザーは何もしない
} else {
saveHistory();
}
この場合、空ブロックそのものに意味があります。
作者としては、
ここは実装漏れではなく、意図的に何もしない
ことを表現したいのかもしれません。
ただしコメントがないと読み手には伝わりにくいため、何もしない理由を残しておく方が親切でしょう。
7. 設計書との対応を維持したい
ウォーターフォール開発などでは、詳細設計書やプログラム設計書に処理フローが記載されていることがあります。
例えば設計書に次のような記述があったとします。
| 条件 | 処理 |
|---|---|
| 管理者の場合 | 何もしない |
| 管理者以外の場合 | 権限チェックを行う |
この設計をそのままコードへ落とし込むと、
if (isAdmin) {
// 何もしない
} else {
checkPermission();
}
となります。
条件反転すればコード量は減りますが、
if (!isAdmin) {
checkPermission();
}
では設計書との対応関係が少し見えにくくなります。
特に設計書とソースコードのトレーサビリティを重視する現場では、あえて設計書の記述順を維持するために空ブロックが残ることがあります。
まとめ
私が見てきたコードでは、空ブロックが積極的に採用されているケースはあまり多くありません。
どちらかというと、
- コーディング規約の影響
- 業務ルールの強調
- 将来実装予定
- リファクタリング途中
- 削除された処理の名残
といった理由で存在していることが多いように感じます。
個人的には、特別な理由がなければ条件反転やガード節を使った方が読みやすいと思っています。
ただ、空ブロックを見つけたときに即座にアンチパターンと決めつけるのではなく、
なぜ作者はこの形を選んだのだろう?
と考えてみると、そのコードが生まれた背景が見えてくるかもしれません。