如何处理 Perl 方法链中的错误?
Posted
技术标签:
【中文标题】如何处理 Perl 方法链中的错误?【英文标题】:How do I handle errors in methods chains in Perl? 【发布时间】:2011-10-27 06:09:40 【问题描述】:在 Perl 中处理方法链中抛出的异常的最佳方法是什么? 如果链接的任何方法抛出异常,我想分配一个值 0 或 undef
代码示例:
my $x = $obj->get_obj->get_other_obj->get_another_obj->do_something;
最好的方法是什么? 我是否必须每次都包含在 try/catch/finally 语句中? 我想应用的上下文是:我在使用 Catalyst 和 DBIC 进行 Web 开发工作,我做了很多链接的结果集,如果其中一些结果集抛出异常,我只想分配一个 0 或 undef 的值,然后处理这个模板中的错误(我正在使用模板工具包)。如果有另一种方法可以做到这一点,而无需将每个调用都包含在 try/catch 中,请告诉我。如果您知道在相同上下文(Catalyst/DBIC/TT)中处理此类错误的更好方法,请提出建议。 一个实际的例子是当用户搜索某些东西但它不存在时。
【问题讨论】:
【参考方案1】:我通过在故障点返回一个空对象来处理这个问题。该对象通过简单地返回自身来响应每个方法,因此它会一直这样做,直到它吃完剩余的方法。最后,您查看$x
,看看它是您预期的结果还是这个空对象。
这是一个这样的例子:
use v5.12;
package Null
my $null = bless , __PACKAGE__;
sub DESTROY 1
sub AUTOLOAD $null
对于每个调用的方法,AUTOLOAD 都会拦截它并返回空对象。
当您遇到错误时,您会返回这些 Null 对象之一。在方法链的中间,您仍然可以返回一个对象,因此当您调用下一个方法时 Perl 不会崩溃。
sub get_other_obj
...;
return Null->new if $error;
...;
在链的末端,您可以检查您返回的内容是否为 Null 对象。如果这就是你得到的结果,那么坏事发生了。
这是基本的想法。您可以改进 Null 类以使其记住消息及其创建位置,或者添加一些多态方法(例如sub is_success 0
)以使其与您期望获得的对象的接口很好地配合。
我以为我在某处写了很长的东西,但现在我找不到了。
更新:找到了其中的一些著作:
The Null Mull No ifs, ands, or buts【讨论】:
这里有一个问题:Setter 方法将使用一个空条目来表示返回当前值。例如,$foo->Name("David") will set the name to
David, and
$foo->Name` 将返回当前名称。因此,一个方法的 null 返回可能是另一个方法的有效输入。
这不是问题。这是一个无操作。随后的方法不做任何事情。您没有将 null 对象作为参数传递;它是参照物。如果前一个方法没有返回对象,则无论如何都不能链接。
对不起,我没听懂。如何在 DBIC 链接结果集中应用它?
我知道这是旧的,但它看起来像什么?这个线程(perlmonks.org/?node_id=899958)似乎表明 Moose/Mouse 人不喜欢返回 undef(默认情况下 perlcritic 也抱怨它),特别注意 stvn 的 cmets。还有,Perl 中的“空对象”是什么?您是在祝福包含 null 的 hashref 还是只是“返回 undef”?
他们在谈论一个明确的return undef
,它在列表上下文中存在问题,因为它是一个项目的列表。我说的是返回一个对象,而其他一切都期望一个对象。【参考方案2】:
您可以编写一个标量方法,在错误处理中包装方法链:
my $try = sub
@_ > 1 or return bless ok => $_[0] => 'Try';
my ($self, $method) = splice @_, 0, 2;
my $ret;
eval
$ret = $self->$method(@_);
1 or return bless error => $@ => 'Try';
bless ok => $ret => 'Try'
;
package Try;
use overload fallback => 1, '""' => sub $_[0]ok;
sub AUTOLOAD
my ($method) = our $AUTOLOAD =~ /([^:]+)$/;
$_[0]ok ? $_[0]ok->$try($method, @_[1..$#_]) : $_[0]
sub DESTROY
sub error $_[0]error
使用它:
package Obj;
sub new bless [0]
sub set $_[0][0] = $_[1]; $_[0]
sub add $_[0][0] += ($_[1] || 1); $_[0]
sub show print "Obj: $_[0][0]\n"
sub dies die "an error occured"
my $obj = Obj->new;
say "ok 1" if $obj->$try(set => 5)->add->add->show; # prints "Obj 7"
# and "ok 1"
say "ok 2" if $obj->$try('dies')->add->add->show; # prints nothing
say $obj->$try('dies')->add->add->show->error; # prints "an error occured..."
$try
方法的第一行还允许使用以下语法:
say "ok 3" if $obj->$try->set(5)->add->add->show;
【讨论】:
【参考方案3】:一个想法是创建一个类,该类使用overload
在字符串/数字/布尔上下文中评估实例对象时返回错误值,但仍允许对其调用方法。 AUTOLOAD
方法总是可以返回 $self
允许方法链传播相同的错误。
【讨论】:
以上是关于如何处理 Perl 方法链中的错误?的主要内容,如果未能解决你的问题,请参考以下文章
在 Sequelize 中使用 .create(...) 方法时如何处理错误