例外クラスの指針
ちゃんと論じようとすると長くなりそう。Javaの理論と実践: 例外をめぐる議論とか、とあるコンサルタントのつぶやき : .NETの例外処理 Part.1から始まる一連のポストとかを見てもわかることだけど。
「エラーなどの種類によって処理を分岐させる場合、種類に対応した例外クラスを作成するべき」
うーん。わかるけど難易度が高いから非現実的かも。
// フィールドに持たせた場合 try { ... ; } catch (FooException ex) { switch (ex.Reason) { case Hoge: ... ; break; case Fuga: ... ; break; default: ... ; // break or rethrow } }
実際これで済む場合もあるわけで、これで済むなら何の問題もない。
エラー理由とその対処の粒度(or 関心事 or 責務)が同じならね。
ビジネスロジックのエラーだったらこんな感じでもいいんじゃ?と思う。
で、これですまない場合なんだけれども。
try { ... ; } catch (HogeException ex) { ... ; } // FugaExceptionは上位層に渡す。
こんなのとか*1。
あとは
try { ... ; } catch (HogeException ex) { // ビジネスロジック的なエラーの対処 } catch (FugaException ex) { // バグとかシステムエラーとかの対処 }
とかね。
で、難しいのは、HogeExceptionとFugaExceptionがどっちもFooBarExceptionを継承しているとか、そんなis-a関係が本当に必要なのか?ってとこだと考える。
つまり、上の例みたいな区別を必要とする場面もあるが、どっちもまとめてcatch(FooBarException ex)で扱いたい場面もある、そんな例外を新たに設計する必要ってどこまである?
もちろん、必要があると信じて設計するんだけど、設計の正しさってよくわからない。
たとえばJavaではSocketException is a IOExceptionだけども、.NETではそうじゃない。これは.NETの例外設計が間違ってるのか?というとそんなこともない。
たとえばJavaではDriverManager.getConnection throws SQLExceptionだけど、DBへの接続文字列が間違ってた場合なんかはIOException派生の別例外でもいいんじゃないのかと思うこともある*2。
……というわけで、俺は
「他の例外クラスを継承しただけの例外クラスを作らない」
をこのように解釈した。やっぱり長くなった。