我会推荐反对#1,因为它会导致更多不会编写的错误处理代码。例如,如果您只是返回 false,那么这可以正常工作。
my $obj = Class->new or die "Construction failed...";
但是如果你返回一个无效的对象......
my $obj = Class->new;
die "Construction failed @{[ $obj->error_message ]}" if $obj->is_valid;
并且随着错误处理代码数量的增加,它被编写的可能性会降低。而且它不是线性的。通过增加错误处理系统的复杂性,实际上可以减少它在实际使用中捕获的错误数量。
您还必须小心,当调用任何方法(除了is_valid
and error_message
)时,您的无效对象会死亡,从而导致更多代码和错误机会。
但我同意能够获取有关失败的信息是有价值的,这使得返回 false (只是return
not return undef
)劣势。传统上,这是通过调用类方法或 DBI 中的全局变量来完成的。
我的 $dbh = DBI->connect($data_source, $username, $password) 或死 $DBI::errstr;
但它的缺点是 A)您仍然必须编写错误处理代码和 B)它仅对最后一个操作有效。
一般来说,最好的办法是用croak
. 现在在正常情况下,用户不编写特殊代码,错误发生在问题点,并且默认情况下他们会收到一个很好的错误消息。
my $obj = Class->new;
Perl 反对在库代码中抛出异常的传统建议已经过时了。Perl 程序员(终于)接受了异常。而不是一遍又一遍地编写错误处理代码,严重且经常忘记异常 DWIM。如果您不相信就开始使用autodie(观看 pjf 的视频),您将永远不会回头。
例外情况使霍夫曼编码与实际使用保持一致。期望构造函数正常工作并且如果不正常则希望出现错误的常见情况现在是最少的代码。想要处理该错误的罕见情况需要编写特殊代码。而且特殊代码很小。
my $obj = eval { Class->new } or do { something else };
如果您发现自己将每个电话都包装在 an 中,eval
那么您做错了。异常被称为是因为它们是异常的。如果,如您在上面的评论中,您希望为了用户的利益而进行优雅的错误处理,那么请利用错误在堆栈中冒泡的事实。例如,如果您想提供一个漂亮的用户错误页面并记录错误,您可以这样做:
eval {
run_the_main_web_code();
} or do {
log_the_error($@);
print_the_pretty_error_page;
};
你只需要它在一个地方,在你的调用堆栈的顶部,而不是分散在任何地方。您可以以较小的增量利用这一点,例如...
my $users = eval { Users->search({ name => $name }) } or do {
...handle an error while finding a user...
};
有两件事正在发生。1)Users->search
总是返回一个真值,在这种情况下是一个数组 ref。这使得简单的my $obj = eval { Class->method } or do
工作。这是可选的。但更重要的是 2) 你只需要在Users->search
. 内部Users->search
调用的所有方法以及它们调用的所有方法......它们只是抛出异常。他们都在某一时刻被抓住并处理了同样的事情。在关心它的地方处理异常可以使错误处理代码更加整洁、紧凑和灵活。
croak
您可以通过使用字符串重载对象而不只是字符串来将更多信息打包到异常中。
my $obj = eval { Class->new }
or die "Construction failed: $@ and there were @{[ $@->num_frobnitz ]} frobnitzes";
例外:
- 做正确的事,不经过调用者的任何思考
- 最常见的情况需要最少的代码
- Provide the most flexibility and information about the failure to the caller
Modules such as Try::Tiny fix most of the hanging issues surrounding using eval
as an exception handler.
As for your use case where you might have a very expensive object and want to try and continue with it partially build... smells like YAGNI to me. Do you really need it? Or you have a bloated object design which is doing too much work too early. IF you do need it, you can put the information necessary to continue the construction in the exception object.