perloopdesign-decisions

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


I am writing a class that is linked to an external resource. One of the methods is a delete method that destroys the external resource. No further method calls should be made on that object. I was thinking of setting a flag and die'ing inside of all of the methods if the flag is set, but is there a better, easier way? Something involving DESTROY maybe?

So far, I am really liking Axeman's suggestion, but using AUTOLOAD because I am too lazy to recreate all of 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;
}

Solution

  • Is there a problem with simply considering the object in an invalid state. If the users hang on to it, isn't that their problem?

    Here are some considerations:

    Here are some suggestions:


    Some proof of concept stuff:

    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