2つの原則
- 検出可能なランタイムエラーをすべて検出し、分類し、報告する
- 検出したエラーを無視できないようにする
失敗したときは、フラグなどの値でなく例外を送ることでプログラムを終了させる。
呼び出したサブルーチンが失敗した場合、そのサブルーチンがエラーを表す値をreturnしても、呼び出し元がそのエラーを無視したり、適切に処理できなければ意味がない。
フラグ等を使用することの欠点は2つある。
1つ目は、開発者(サブルーチンを使用して、呼び出し元コードを欠いている人)がエラーを無視することに慣れること。
2つ目は、何重にもネストして呼び出されたサブルーチンは、一番最初の呼び出し元まで、バケツリレーのように、エラーを適切に処理&上位に送信、を繰り返さなければならない。これでは、すべてのサブルーチンのコードが肥大することになる。バケツリレーでエラーを運ぶのではなく、失敗したサブルーチン内で例外を出すこと。
この例外をスルーするにはevalを明示的に使用する努力が呼び出し元に要求される。そして、この例外を一番先頭の呼び出し元で補足することで、各ルーチンすべてにバケツリレー処理を書く必要がなくなる。バケツリレー処理を書かないで済む理由は、例外は自動的にバケツリレーをして、上位呼び出し元に運んでくれるから。そして最上位呼び出し元で、明示的に例外が補足されないとプログラムは終了する。
以下のように、ネストではなく、並列に並んだ処理のすべての値をチェックしたい場合も、例外を使用したほうがいい。
コードがすっきりする。
SOURCE_FILE:
for my $filename (@source_files) {
my $fh = locate_and_open($filename);
next SOURCE_FILE if !defined $fh; #このエラー処理を無くしたい
my $head = load_header_from($fh);
next SOURCE_FILE if !defined $head; #このエラー処理を無くしたい
print $head;
}
for my $filename (@directory_path) {
#例外が出されたときは、evalによって無視されるので、nextと同じ動作になる。
eval {
my $fh = locate_and_open($filename); #サブルーチン内でエラー処理。その際、例外を出すようにする。
my $head = load_header_from($fh); #サブルーチン内でエラー処理。その際、例外を出すようにする。
print $head;
}
}
組み込み関数のエラーで、例外を出させるようにする
Fatalモジュール
組み込み関数や、サブルーチンの名前を指定すると、falseを返すのではなく、例外が出されるように黒魔術がかけられる。
その際、:voidという特殊なマーカを使用しない。これは、voidコンテキスト以外ではFatalが例外を送出しないようにするためのマーカ。紛らわしいので使わない。
やっかいなsystem組み込み関数
systemコマンドは、成功するとfalseを返し、失敗するとtrueを返す。
これはFatalも効かない。POSIX標準モジュールのWIFEXITEDサブルーチンを使用するか、Perl6モジュールを使用する。(Perl6では、system関数は成功するとture、失敗するとfalseを出すように修正される)
use POSIX qw( WIFEXITED );
WIFEXITED(system $cmd)
or croak "Couldn't run: $cmd ($OS_ERROR)";
use Perl6::Builtins qw( system );
system $cmd
or croak "Couldn't run: $cmd ($OS_ERROR)";
回復可能なエラーでも例外を送出する
回復可能なエラーではundefを使用した方が良いと思われるが、ここでも例外を送出したほうが良い。理由は、今までと同じで、開発者がエラー処理をするとは限らないから。(フラグ等、戻り値は無視される)
例外は呼び出し元から出す
サブルーチン内でエラーを返されても役に立たない。呼び出し元からエラーを送出すること。
理由は、あなたが作ったサブルーチンを使用している開発者が知りたいのは、あたたのコードのどこで問題が検出されたのかではなく、自分たちのコードのどこに問題があるか、だから。
dieではなくcroak( )を使用する
呼び出されたサブルーチンの立場でエラーを報告するdieを使って例外を出すのは、呼び出されたサブルーチンに落ち度があるときだけ。そのときのメッセージは常に"Internal error:"で始まるようにすること。その他の場合はcroak( )を使用すること。
しかし、内部エラーであってもcroak( )を使用したほうが良いときもある。例えば、内部エラーを呼び出し元の視点に立って報告することが出来る点。さらに重要なのはCarpモジュールにコマンドラインから使用できる'verbose'オプションがあること。このオプションを使用してプログラムを実行すると、croak( )の呼び出しごとに、エラーメッセージと完全なバックトレースが提供される。
組み込みwarnではなく、carp( )を使用する
dieと同じ理由。警告でもcarp( )を使用すること。
0 件のコメント:
コメントを投稿