你的思维水平不够高。好的,构建器失败了。该属性仍未定义。但是你对调用访问器的代码怎么办?该类的契约表明调用该方法将始终返回一个 IO::File。但现在它正在返回 undef。(合同是IO::File
,不是Maybe[IO::File]
,对吧?)
因此,在下一行代码中,调用者将死去(“无法在 the_caller.pl 第 42 行的未定义值上调用方法 'readline'。”),因为它希望您的类遵循它定义的契约. 失败不是你的班级应该做的事情,但现在它做到了。调用者如何做任何事情来纠正这个问题?
如果它可以处理undef
,调用者实际上并不需要一个文件句柄,那么它为什么要向你的对象要一个文件句柄呢?
考虑到这一点,唯一理智的解决办法就是死。您无法履行您同意的合同,die
这是您摆脱这种情况的唯一途径。所以就这样做吧;死亡是生命的事实。
现在,如果您不准备在构建器运行时死掉,那么您需要更改可能失败的代码何时运行。您可以在对象构造时执行此操作,方法是使其非惰性,或者通过显式激活 BUILD ( BUILD { $self->file_name }
) 中的属性。
更好的选择是根本不向外界公开文件句柄,而是执行以下操作:
# dies when it can't write to the log file
method write_log {
use autodie ':file'; # you want "say" to die when the disk runs out of space, right?
my $fh = $self->file_handle;
say {$fh} $_ for $self->log_messages;
}
现在你知道程序什么时候死了;在new
, 或 在write_log
. 你知道,因为文档是这样说的。
第二种方式让你的代码更干净;消费者不需要知道你的类的实现,它只需要知道它可以告诉它写一些日志消息。现在调用者不关心你的实现细节;它只是告诉班级它真正想要做什么。
而且,死亡write_log
甚至可能是你可以从中恢复的东西(在一个 catch 块中),而“无法打开这个你无论如何都不应该知道的随机不透明的东西”对于调用者来说更难恢复。
基本上,理智地设计你的代码,例外是唯一的答案。
(无论如何,我并没有完全理解“它们是杂种”。它们在 C++ 中的工作方式完全相同,在 Java、Haskell 和其他所有语言中也非常相似。这个词die
真的那么可怕吗?)