5

特定の値がコンストラクターに渡された場合にのみ、いくつかのメソッドがインスタンスに存在するようにクラスを作成するにはどうすればよいですか?

おそらく、より一般的な質問方法は次のとおりです。既存のクラス インスタンスにメソッドを追加するにはどうすればよいですか?

4

6 に答える 6

3

フラグに基づいて、匿名サブをオブジェクトにアタッチできます。

use strict;
use warnings;
package Object;
sub new {
   my $class = shift;
   my $self = bless {}, $class;
   my %args = @_; 
   if ($args{method}) {
       $self->{method} = sub { print "hello\n" }
   }   
   return $self;
}

sub method {
    my $self = shift;
    if (not defined $self->{method}) {
        warn "Not bound\n";
        return;
    }   
    $self->{method}->();
}
1;

使用する:

use Object;
my $obj1 = Object->new(method=>1);
$obj1->method();
my $obj2 = Object->new();
$obj2->method();

同じインターフェースを介して、これをいくつかのメソッドに拡張できます。

于 2013-04-16T19:31:05.557 に答える
1

メソッドはパッケージ内の単なるサブルーチンであり、パッケージは型グロブを保持する単なるハッシュです。また、ハッシュは実行時に変更できます。

したがって、理論的には、コンストラクターで値を指定してメソッドを追加または削除できます。

package WeirdClass;

sub new {
  my ($class, $name, $code) = @_;
  if ($name) {
    no strict;
    *{__PACKAGE__ . "::$name"} = $code;
  }
  bless {} => $class;
}

そして、次のように使用します。

my $object = WeirdClass->new(foo => sub {say "foo"});
$object->foo(); # prints "foo\n";

ただし、このメソッドはそのクラスのすべてのオブジェクトで使用できます。

my $another_object = WeirdClass->new();
$another_object->foo; # works too.

autoload を使用すると、任意のメソッドをモックできます。

package BetterClass;

sub new {
  my ($class, %args) = @_;
  bless \%args => $class;
}

# destructor will be called at cleanup, catch with empty implementation
sub DESTROY {}; 

sub AUTOLOAD {
  my $self = shift;
  (my $method = our $AUTOLOAD) =~ s/.*://; #  $AUTOLOAD is like "BetterClass::foo"

  # check if method is allowed
  die "forbidden method $method" unless $self->{can}{$method};

  # mock implementations
  given ($method) {
    say "foo" when "foo";
    say "bar" when "bar";
    when ("add") {
      my ($x, $y) = @_;
      return $x + $y;
    }
    default { die "unknown method $method" }
  }
}

それで:

my $o = BetterClass->new(can => { foo => 1, bar => 0});
$o->foo;
my $p = BetterClass->new(can => {bar => 1, add => 1});
$p->bar;
say $p->add(5, 6);

もちろん、これらの技術は自由に組み合わせることができます。


編集:can()

canを使用するAUTOLOADには、保護されたメソッドをデータ構造に移動する必要があります。

my %methods;
BEGIN {
  %methods = (
    foo => sub {say "foo"},
    bar => sub {say "bar"},
    add => sub {
      my ($self, $x, $y) = @_;
      $x + $y;
    },
  );
}

can次に、メソッドをオーバーライドします。

# save a reference to the origional `can` before we override
my $orig_can;
BEGIN{ $orig_can = __PACKAGE__->can("can") }

sub can {
  my ($self, $meth) = @_;

  # check if we have a special method
  my $code = $methods{$meth} if ref $self and $self->{can}{$meth};
  return $code if $code;

  # check if we have a normal method
  return $self->$orig_can($meth);
}

そして、AUTOLOADに変わります

my ($self) = @_; # do not `shift`
(my $method = our $AUTOLOAD) =~ s/.*://;
my $code = $self->can($method) or die "unknown method $method";
goto &$code; # special goto. This is a AUTOLOAD idiom, and avoids extra call stack frames
于 2013-04-16T19:38:08.647 に答える
1

魔法をかけすぎないでください。AUTOLOAD謎のメソッドが突然現れたり消えたりするメンテナンスの問題を引き起こすため、私は回避しました。

必要なものを処理する 1 つの方法は、必要なすべてのメソッドを定義することです。特定のオブジェクトの型が間違っている場合は、単にそのメソッドをcroakさせます。

sub Foo {
    my $self       = shift;
    my $parameter  = shift;

    if ( $self->Class_type ne "Foo" ) {
        croak qq(Invalid method 'Foo' on object @{[ref $self]});
    }
    print "here be dragons\";
    return "Method 'Foo' successfully called";
}

上記では、クラス タイプが でFooない限り、メソッドを呼び出すことはできません。Foo

オブジェクトが作成された後、オブジェクトが変更されない (または変更したくない) 場合は、そのオブジェクトをサブクラスとして定義できます。

新しく作成されたオブジェクトを祝福する前に、その特別な値を確認し、代わりに特定のサブクラスを作成する必要があるかどうかを判断してください。

package My_class;

sub new {
    my $class      = shift;
    my $class_type = shift;

    my $self = shift;

   if ( $class_type eq "Foo" ) {
      bless $self, "My_class::Foo";
   }
   else {
     bless $self, $class;
   }

package My_class::Foo;
use base qw(My_class);

sub Foo {
    my $self = shift;
    return "Foo Method successfully called!";
}

私のクラスはプラグマ経由のMy_class::Fooサブクラスであることに注意してください。これは、 のすべてのメソッドが のオブジェクトに対して有効であることを意味します。ただし、メソッドを呼び出すことができるのは のオブジェクトだけです。My_classuse baseMy_classMy_class::FooMy_class::FooFoo

new(サブルーチンを使用して) オブジェクトを作成するときは、$class_typeパラメーターを確認します。type の場合FooblessクラスはMy_class::Foo.

サブクラスを使用して必要なことを行う例を次に示します。

すべてのオブジェクトは のクラス タイプですQuestion。コンストラクターは 1129 行目にあります。パラメーターの 1 つとして質問タイプをコンストラクターに渡します。

1174 行から 1176 行で、オブジェクトを作成しますが、質問タイプをクラスに追加し、質問をそのサブクラス タイプとして祝福します。私のサブクラスはすべて型ですQuestion(use base qw(Question);package宣言の下の my を参照してください。ただし、サブクラスの質問のみがメソッドQuestion::Dateを持ちます。また、型のオブジェクトのみがメソッドを持ちます。Question::RegexFormatQuestion::WordsForce

お役に立てれば。

于 2013-04-16T20:19:29.250 に答える
1

これまでに与えられた回答のどれも、実際に尋ねられた質問を実際に処理していません。

Perl のインスタンスへのメソッドの追加は直接サポートされていません。オブジェクト インスタンスは常に何らかのクラスのインスタンスであり、そのクラスは実際にメソッドを持つものです。同じクラスの他のすべてのインスタンスでもそのメソッドを使用できるようにしない限り、クラスの単一のインスタンスにメソッドを追加することはできません。

あなたの問題には、2 つの基本的な解決策があります。

  1. メソッドは常に提供しますが、フラグをテストして、メソッドが特定のインスタンスに適用されるかどうかを確認します。これは最も単純です。

  2. フラグに応じて、各オブジェクトをサブクラスに分類します。メイン クラスをサブクラス化して、必要に応じてこれらのメソッドを提供します。

個々のインスタンスにメソッドを本当に追加したい場合は、すべてのインスタンスがすべてのオブジェクトの新しく派生したクラスの単一のインスタンスになるように調整する必要があります。DESTROYオブジェクトが編集された後にメモリリークやクラスのクリーンアップを避けたい場合は、二重にこれを調整するのが難しくなります。ただし、これにより、真にインスタンスごとのメソッドが可能になります。

この 3 番目のオプションが本当に必要になる可能性はほとんどないため、最初のオプションのいずれかを使用する方がはるかに優れています。

于 2013-04-21T17:04:12.497 に答える