21-技術的例外とビジネス例外を明確に区別する

プログラマが知るべき97のこと」の21個目のエピソードは、例外に関する話題です。このエピソードは具体的な内容なので書くべき事はあまりありませんが、例外に関して適切に設計されているかどうかは、その開発チームのレベルがハッキリと解る部分とも言えます。全く目が当てられないようなコードはともかく、デザインパターンを頑張って適用したんだなと解るコードで例外に関して無茶苦茶なコードを見たことはないでしょうか?デザインパターンはハウツー本なども多く出ており何となく格好良く、そして上級者になった気にさせるスキルです。デザインパターンが悪いわけではありませんが、背伸びして使おうとしているのは良く見かける傾向です。一方で、例外のハンドリングは地味なスキルです。しかし、例外が適切に処理されているコードは品質も高く、何か問題が発生しても原因を簡単に突き止めることができるでしょう。そんないぶし銀的なスキルなのです。
このエピソードでは、例外を技術的例外とビジネス例外の2つに区別することが重要であると書かれています。技術的な例外とは一言で言えばバグです。配列の範囲外へのアクセスやNPE(NullPointerException)など、利用する側のコードに問題があって発生する例外です。このような例外が発生するからと言って次のようなコードを書いてはいけません。

try {
  doHogehoge();
} catch(Exception e) {
  // NPEが発生する不具合に対応
}

いわゆる「例外の握りつぶし」と呼ばれるアンチパターンです。Javaでは技術的な例外の多くはRuntimeExceptionでthrowされるため、catchやthrows句に宣言する必要はありません。技術的例外はフレームワーク層まで到達させ、適切な処理をします。勝手にハンドリングするくらいならば、原因を見つけて修正すべきです。利用しているライブラリの不具合ならば仕方ありませんが、その場合はせめてログに吐くなどの事を行いましょう。

技術的な例外に対してビジネス例外とは、業務ロジック上で起こりえる正常ではない処理で発生させます。ユースケース等では正常系に対し、準正常系とか代替ケース(シナリオ)などと呼びます。テスト的な視点で言えば、正常系に対し準正常系と呼ばれる事が多いでしょう。代表的な例はログイン時にパスワードが一致しないケースです。このような業務ロジック上で検知したならばnullや異常コードで呼び出し元に伝えるのではなく、例外を使用します。この時に利用する例外がビジネス例外です。
ビジネス例外は適切に処理をしなければなりません。したがって、Javaの場合はcatchまたはthrowsを強制する非実行時例外とするべきでしょう。すべてを実行時例外としてしまい可読性を高める選択肢もありますが、ハンドリングを忘れるリスクがあるので注意すべきです*1

また、Javaをはじめ継承をサポートしている言語であれば、例外の階層構造を考慮することで簡単に区別することができます。更に例外の運用に関する標準を作っておけば、適切に例外を運用できるでしょう。例外は悪い奴ではありません。NPEは適切にアプリケーションの問題を通知してくれるのです。悪者扱いしてなかったことにしないでください。

プログラマが知るべき97のこと

プログラマが知るべき97のこと

*1:少なくともテストを行わない現場やスキルレベルの低いメンバーがいる中で工数削減の為に行うべきではない