4

外部リソースにリンクされているクラスを書いています。メソッドの1つは、外部リソースを破棄するdeleteメソッドです。そのオブジェクトに対してそれ以上のメソッド呼び出しを行うべきではありません。フラグを設定して、フラグが設定されている場合はすべてのメソッドの内部で死ぬことを考えていましたが、より良い、より簡単な方法はありますか?多分破壊を含む何か?

これまでのところ、私は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;
}
4

5 に答える 5

6

単にオブジェクトを無効な状態と見なすことに問題がありますか?ユーザーがそれに固執する場合、それは彼らの問題ではありませんか?

ここにいくつかの考慮事項があります:

  • あなたはそれが死ぬ価値があるかどうかをすでに決めましたか?

  • 十分にカプセル化された関数がある場合、ユーザーにコードを解析させたくない可能性があります。その目的のために、私が「先に進み、失敗させる」パターンと呼んでいるものを使用したくないでしょう。'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
于 2009-05-01T21:10:25.707 に答える
2

理想的には、範囲外になるはずです。何らかの理由で適切なスコープを削除できず、参照が誤ってリソースをアクティブに保つことが心配な場合は、弱い参照を検討する可能性があります(Scalar :: Utilは少なくとも5.10のコアです)。

于 2009-05-01T21:13:31.067 に答える
2

モジュール内に単一の強力な参照を保持して、ユーザーにオブジェクトへのweakrefのみを取得させることができます。次に、リソースが破棄されたら、強力な参照とプーフを削除します。これ以上オブジェクトは削除しません。

于 2009-05-01T21:14:50.657 に答える
1

ここでの最初の回答のコメントに続いて、 Mooseでオブジェクトの動作を修正する「1つの方法」があります。

{
    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"

Moose で目的を達成する方法は他にもあります。しかし、私はこの役割のアプローチが好きです。

確かに考えさせられます。

于 2009-05-03T18:20:58.670 に答える
0

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 の現在および新しいインスタンスはすべて、現在の状態が何であれ反映されます。

于 2009-05-03T11:15:18.833 に答える