我正在编写一个链接到外部资源的类。其中一种方法是破坏外部资源的删除方法。不应该对该对象进行进一步的方法调用。如果设置了标志,我想在所有方法中设置一个标志和死亡,但是有更好,更简单的方法吗?可能涉及DESTROY的东西?
到目前为止,我真的很喜欢Axeman的建议,但使用AUTOLOAD是因为我懒得重新创建所有方法:
#!/usr/bin/perl
use strict;
use warnings;
my $er = ExternalResource->new;
$er->meth1;
$er->meth2;
$er->delete;
$er->meth1;
$er->meth2;
$er->undelete;
$er->meth1;
$er->meth2;
$er->delete;
$er->meth1;
$er->meth2;
$er->meth3;
package ExternalResource;
use strict;
use warnings;
sub new {
my $class = shift;
return bless {}, $class;
}
sub meth1 {
my $self = shift;
print "in meth1\n";
}
sub meth2 {
my $self = shift;
print "in meth2\n";
}
sub delete {
my $self = shift;
$self->{orig_class} = ref $self;
return bless $self, "ExternalResource::Dead";
}
package ExternalResource::Dead;
use strict;
use Carp;
our $AUTOLOAD;
BEGIN {
our %methods = map { $_ => 1 } qw/meth1 meth2 delete new/;
}
our %methods;
sub undelete {
my $self = shift;
#do whatever needs to be done to undelete resource
return bless $self, $self->{orig_class};
}
sub AUTOLOAD {
my $meth = (split /::/, $AUTOLOAD)[-1];
croak "$meth is not a method for this object"
unless $methods{$meth};
carp "can't call $meth on object because it has been deleted";
return 0;
}
答案 0 :(得分:6)
简单地将对象视为无效状态是否存在问题。如果用户坚持使用它,那不是他们的问题吗?
以下是一些注意事项:
你已经决定是否值得死了吗?
如果你有一个足够封装的功能,你真的不希望让用户解析你的代码。为此,你可能不想使用我称之为Go-ahead-and-it-it-fail模式。 'Can't call method "do_your_stuff" on an undefined value'
可能无法用于封装目的。除非你告诉他们“嘿你删除了对象!
以下是一些建议:
您可以将对象重新到一个类中,该类的唯一作用是指示无效状态。它具有相同的基本形式,但表中的所有符号都指向一个只是说“抱歉不能做,我已经关闭(你关闭我,记得吗?)”的子。“
< / LI>您可以在删除中取消$_[0]
。然后他们从他们的代码中的一行得到一个很好的'Can't call method "read_from_thing" on an undefined value'
- 前提是他们没有经过精心设计的装饰或委派过程。但正如混乱所指出的那样,这并没有清除多个参考文献(因为我已经通过下面的示例代码进行了调整以显示)。
概念证明:
use feature 'say';
package A;
sub speak { say 'Meow!'; }
sub done { undef $_[0]; }
package B;
sub new { return bless {}, shift; }
sub speak { say 'Ruff!' }
sub done { bless shift, 'A'; }
package main;
my $a = B->new();
my $b = $a;
$a->speak(); # Ruff!
$b->speak(); # Ruff!
$a->done();
$a->speak(); # Meow!
$b->speak(); # Meow! <- $b made the switch
$a->done();
$b->speak(); # Meow!
$a->speak(); # Can't call method "speak" on an undefined value at - line 28
答案 1 :(得分:2)
理想情况下,它应该超出范围。如果由于某种原因,无法删除适当的范围,并且您担心引用会意外地保持资源处于活动状态,则可能会考虑弱引用(Scalar :: Util至少是5.10中的核心)。
答案 2 :(得分:2)
您可以让用户只获得weakrefs对象,并在模块中保留单个强引用。然后,当资源被销毁时,删除强引用和poof,不再删除对象。
答案 3 :(得分:1)
在我的第一个答案中的评论之后是使用Moose修改对象行为的“单向方法”。
{
package ExternalResource;
use Moose;
with 'DefaultState';
no Moose;
}
{
package DefaultState;
use Moose::Role;
sub meth1 {
my $self = shift;
print "in meth1\n";
}
sub meth2 {
my $self = shift;
print "in meth2\n";
}
no Moose::Role;
}
{
package DeletedState;
use Moose::Role;
sub meth1 { print "meth1 no longer available!\n" }
sub meth2 { print "meth2 no longer available!\n" }
no Moose::Role;
}
my $er = ExternalResource->new;
$er->meth1; # => "in meth1"
$er->meth2; # => "in meth2"
DeletedState->meta->apply( $er );
my $er2 = ExternalResource->new;
$er2->meth1; # => "in meth1" (role not applied to $er2 object)
$er->meth1; # => "meth1 no longer available!"
$er->meth2; # => "meth2 no longer available!"
DefaultState->meta->apply( $er );
$er2->meth1; # => "in meth1"
$er->meth1; # => "in meth1"
$er->meth2; # => "in meth2"
还有其他方法可以实现你在穆斯的追求。不过我确实喜欢这种roles方法。
当然值得深思。
答案 4 :(得分:0)
package ExternalResource;
use Moose;
use Carp;
sub meth1 {
my $self = shift;
print "in meth1\n";
}
sub meth2 {
my $self = shift;
print "in meth2\n";
}
sub delete {
my $self = shift;
my %copy; # keeps copy of original subref
my @methods = grep { $_ ne 'meta' } $self->meta->get_method_list;
for my $meth (@methods) {
$copy{ $meth } = \&$meth;
$self->meta->remove_method( $meth );
$self->meta->add_method( $meth => sub {
carp "can't call $meth on object because it has been deleted";
return 0;
});
}
$self->meta->add_method( undelete => sub {
my $self = shift;
for my $meth (@methods) {
$self->meta->remove_method( $meth );
$self->meta->add_method( $meth => $copy{ $meth } );
}
$self->meta->remove_method( 'undelete' );
});
}
现在,ExternalResource的所有当前和新实例都将反映当前状态。