What should I do with an object that should no longer be used in Perl?

I am writing a class associated with an external resource. One method is the delete method, which destroys an external resource. No further method calls should be made on this object. I was thinking about setting a flag and dying in all methods if the flag is set, but is there a better, easier way? Perhaps something related to DESTROY?

So far I really like the Axeman suggestion, but using AUTOLOAD because I'm too lazy to recreate all the methods:

#!/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; } 
+4
oop perl design-decisions
source share
5 answers

Is there a problem with simply examining an object in an invalid state. If users freeze, is this not a problem?

Here are some considerations:

  • Have you decided whether to die?

  • Most likely, if you have a function that is sufficiently encapsulated, you really do not want users to analyze your code. For this purpose, you probably would not want to use what I call the Go-ahead-and-let-it-fail pattern. 'Can't call method "do_your_stuff" on an undefined value' probably won't work for encapsulation purposes either. If you don’t tell them, β€œHey, you deleted the item!

Here are some suggestions:

  • You can drop an object into a class whose only job is to specify an invalid state. It has the same basic form, but all the characters in the table indicate a subclass that simply says: "Sorry, I can’t do this, I was closed (you closed me, remember?)."

    / li>
  • You can undef $_[0] delete. Then they get a good 'Can't call method "read_from_thing" on an undefined value' from a line in their code - provided that they do not go through a complicated decoration or delegation process. But, as indicated in the chaos, this does not clarify more than one link (since I applied the code example below to show).


Some evidence for the concept:

 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 
+6
source share

Ideally, it should go beyond. If for some reason the proper coverage cannot be outlined, and you are worried that links accidentally supporting the resource may consider weak links (Scalar :: Util has a kernel of at least 5.10).

+2
source share

You can force users to get weakrefs to an object, with one strong link stored inside your module. Then, when the resource is destroyed, delete the strong link and poof, more objects.

+2
source share

Following the comments in my first answer, here is the "one way" to change the behavior of an object using 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" 

There are other ways to probably achieve what you did at the Muse. However, I like roles .

Of course, food for thought.

+1
source share

With Moose, you can change the class using MOP :

 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' ); }); } 

Now all current and new instances of ExternalResource will reflect the entire current state.

0
source share

All Articles