Что мне делать с объектом, который больше не должен использоваться в Perl? - PullRequest
4 голосов
/ 02 мая 2009

Я пишу класс, который связан с внешним ресурсом. Одним из методов является метод delete, который уничтожает внешний ресурс. Никаких дальнейших вызовов метода не должно быть сделано для этого объекта. Я думал об установке флага и смерти внутри всех методов, если флаг установлен, но есть ли лучший, более простой способ? Может быть, что-то, связанное с DESTROY?

Пока что мне действительно нравится предложение Аксемана, но я использую 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;
}

Ответы [ 5 ]

6 голосов
/ 02 мая 2009

Есть ли проблема с простым рассмотрением объекта в недопустимом состоянии. Если пользователи держатся за это, разве это не их проблема?

Вот несколько соображений:

  • Вы уже решили, стоит ли умирать?

  • Скорее всего, если у вас есть достаточно инкапсулированная функция, вы действительно не хотите, чтобы пользователи анализировали ваш код. Для этой цели вам, вероятно, не хотелось бы использовать то, что я называю паттерном «давай-и-давай-проваливай». 'Can't call method "do_your_stuff" on an undefined value', вероятно, не будет работать в целях инкапсуляции. Если вы не скажете им: "Эй, ты удалил объект!

Вот несколько предложений:

  • Вы можете rebless объект в класс, единственной задачей которого является указание недопустимого состояния. Он имеет ту же базовую форму, но все символы в таблице указывают на подпрограмму, которая просто говорит: «Извините, я не могу этого сделать, меня закрыли (Вы меня закрыли, помните?).» *

  • Вы можете отменить $_[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
2 голосов
/ 02 мая 2009

Вы можете сделать так, чтобы пользователи получали только слабых ссылок на объект, при этом внутри вашего модуля сохраняется единственная сильная ссылка. Затем, когда ресурс уничтожен, удалите сильную ссылку и пуф, больше никаких объектов.

2 голосов
/ 02 мая 2009

В идеале, оно должно выпасть из области видимости. Если по какой-то причине правильная область не может быть разграничена, и вы беспокоитесь о ссылках, случайно сохраняющих ресурс активным, возможно, стоит рассмотреть слабые ссылки (Scalar :: Util является ядром по крайней мере 5.10).

1 голос
/ 03 мая 2009

Исходя из комментариев в моем первом ответе, здесь есть "один способ" изменить поведение объекта с помощью 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"

Существуют и другие способы достижения того, что вам нужно в Музе. Однако мне нравится этот подход ролей .

Конечно, пища для размышлений.

0 голосов
/ 03 мая 2009

С помощью Moose вы можете изменить класс, используя 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' );
    });
}

Теперь все текущие и новые экземпляры ExternalResource будут отражать текущее состояние.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...