ちょっとした愚痴のようなものです。真に受けないでください。
例えばこういうのは例外ではなく論外である。
- XXXCommonException
- 「なんでもかんでもこの例外でとりあえず上げる」というタイプ。即時殺処分すべき。
- ただし「とりあえずswitchのdefaultみたいなノリで拾えるようにしたいからこれを継承しろ」というやつで、これがabstractであるなら許す。大抵そんな頭はない。
- 「なんでもかんでもこの例外でとりあえず上げる」というタイプ。即時殺処分すべき。
- XXXWarningException
- ログレベルを例外で管理したいという発想は分からないでもないのだが、それならマーカーインターフェースなりアノテーションなりもっと使うべきものがある。
- せめて上記のCommon同様にabstractならよいのだが、もちろんそんな頭はない。
- ログレベルを例外で管理したいという発想は分からないでもないのだが、それならマーカーインターフェースなりアノテーションなりもっと使うべきものがある。
なんで駄目なのさ?
そもそも「例外」という機構は、「これこれこういう理由で処理がぶっ飛んだ時どうしますか?」という記述を可能にするために生まれたものである(といっても怒られんと思う)。
送出される「例外」というものはまさにその「これこれこういう理由」であり、「なんちゃらの一般的事由で処理が吹っ飛びました」とか言われても「ふざけんな」としか返せないのだ。
わかった、じゃあそれぞれの理由をそれっぽく分類して例外クラスを作ればいいのか?
よくない。もちろん全く駄目だとは言わないが、その前にやることがある。
責務の決定
「誰が、どこまで、何をするか」を決めておくということ。
例えば「あるファイルを読み取ってパースし、オブジェクトとして保持する」という操作があったとする。
この時(やや細かめだが)「あるファイルの場所を渡すやつ」「あるファイルを読むやつ」「読んだものをパースするやつ」「保持するやつ」の4人が登場人物として現れる。
はたしてこいつらは何をどこまですべきだろうか?
常識的に考えると、「あるファイルの場所を渡すやつ」は、少なくとも「ファイルの場所であろうものを」「(おそらくは)文字列で」「欠損なく」渡してくれるものであると仮定する必要があるだろう。
この仮定の基に考えると、「あるファイルを読むやつ」はいちいち「このファイルの場所っぽいものは本当にファイルの場所を示しているんだろうか?」なんて余計なことを考えずに済むし、いざ読みに行ってファイルが無かったとしても「おい、お前が渡してきた情報で探しに行ったけどなかったじゃねーか!」と文句(=例外)をいうことができる。
なおこれをかっこよくいうとCoC、契約による設計というやつになる。
何、「例外関係ないじゃないか!」って?何を言っているんだ。例外は契約が守られない時に使うものだと言っているんだよ。
今あるものの活用
引数が正しくないならおとなしくIllegalArgumentException投げろよ。以上。
……流石に雑すぎるのでもう少し掘り下げる。
例えば以下のようなAPIを準備するとする。2つの引数をとり、a/bの結果を返すというものだ。
(所詮例示なので、あまりにもあんまりな代物なのは勘弁してほしい)
public int div(int a, int b){
return a/b;
}
しかしこれはよろしくない、と批判を受けた。なぜならdiv(1,0)
とでもするとArithmeticException(.netの民にはDivideByZeroExceptionと言ったほうが通りがいいかもしれない、かくいう私もDBZE派……)が起こってしまうからだ。
そうするとどうするか?まあ、バリデーションを用意することになろう。
さて、ここでわざわざ新規の例外クラスを作成すべきだろうか?
答えはもちろん「場合による」である。
たとえば先の例のように、「個々の例外をある程度まとめて引っ掛ける」用途が想定されるのならば、これは作成すべきであろう。多数の例外を引っ掛けるのはやはり見苦しいものだ。
逆に言えば、何かしら作る理由がないのなら、今あるものを使うべきである。
難しいことではない。要するに車輪の再発明は馬鹿げているからやめろというだけのことでしかない。
まとめ
契約を守って楽しくコーディング!!
あと、頼むから自分から型システムぶっ壊しに行く真似だけはやめろ。
(もっとも、どっちかというとこれはコード値の類をナマの数字で扱うおバカに言うべきな気はするが……)