特定の値がコンストラクターに渡された場合にのみ、いくつかのメソッドがインスタンスに存在するようにクラスを作成するにはどうすればよいですか?
おそらく、より一般的な質問方法は次のとおりです。既存のクラス インスタンスにメソッドを追加するにはどうすればよいですか?
フラグに基づいて、匿名サブをオブジェクトにアタッチできます。
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();
同じインターフェースを介して、これをいくつかのメソッドに拡張できます。
メソッドはパッケージ内の単なるサブルーチンであり、パッケージは型グロブを保持する単なるハッシュです。また、ハッシュは実行時に変更できます。
したがって、理論的には、コンストラクターで値を指定してメソッドを追加または削除できます。
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
魔法をかけすぎないでください。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_class
use base
My_class
My_class::Foo
My_class::Foo
Foo
new
(サブルーチンを使用して) オブジェクトを作成するときは、$class_type
パラメーターを確認します。type の場合Foo
、bless
クラスはMy_class::Foo
.
サブクラスを使用して必要なことを行う例を次に示します。
すべてのオブジェクトは のクラス タイプですQuestion
。コンストラクターは 1129 行目にあります。パラメーターの 1 つとして質問タイプをコンストラクターに渡します。
1174 行から 1176 行で、オブジェクトを作成しますが、質問タイプをクラスに追加し、質問をそのサブクラス タイプとして祝福します。私のサブクラスはすべて型ですQuestion
(use base qw(Question);
各package
宣言の下の my を参照してください。ただし、サブクラスの質問のみがメソッドQuestion::Date
を持ちます。また、型のオブジェクトのみがメソッドを持ちます。Question::Regex
Format
Question::Words
Force
お役に立てれば。
これまでに与えられた回答のどれも、実際に尋ねられた質問を実際に処理していません。
Perl のインスタンスへのメソッドの追加は直接サポートされていません。オブジェクト インスタンスは常に何らかのクラスのインスタンスであり、そのクラスは実際にメソッドを持つものです。同じクラスの他のすべてのインスタンスでもそのメソッドを使用できるようにしない限り、クラスの単一のインスタンスにメソッドを追加することはできません。
あなたの問題には、2 つの基本的な解決策があります。
メソッドは常に提供しますが、フラグをテストして、メソッドが特定のインスタンスに適用されるかどうかを確認します。これは最も単純です。
フラグに応じて、各オブジェクトをサブクラスに分類します。メイン クラスをサブクラス化して、必要に応じてこれらのメソッドを提供します。
個々のインスタンスにメソッドを本当に追加したい場合は、すべてのインスタンスがすべてのオブジェクトの新しく派生したクラスの単一のインスタンスになるように調整する必要があります。DESTROY
オブジェクトが編集された後にメモリリークやクラスのクリーンアップを避けたい場合は、二重にこれを調整するのが難しくなります。ただし、これにより、真にインスタンスごとのメソッドが可能になります。
この 3 番目のオプションが本当に必要になる可能性はほとんどないため、最初のオプションのいずれかを使用する方がはるかに優れています。