23

Perl でクラス コンストラクターから基本コンストラクターを呼び出す正しい方法は何ですか?

私は次のような構文を見てきました:

 my $class = shift; 
 my $a = shift; 
 my $b = shift;
 my $self = $class->SUPER::new($a, $b);
 return $self;

これは正しいです ?いくつかの親クラスがある場合はどうでしょう。たとえば、次のようなクラスです。

 package Gamma;
 use base Alpha;
 use base Beta;

 sub new
 {
   # Call base constructors...
 }
 1;
4

3 に答える 3

33

コンストラクターが実行しているのは親コンストラクターの呼び出しだけである場合 (例のように、作成する必要はまったくありません。単純に省略して親が呼び出されます。オブジェクトが bless されていることを確認するだけで済みます。正しいタイプ:

package Parent;
use strict;
use warnings;

sub new
{
    my ($class, @args) = @_;

    # do something with @args

    return bless {}, $class;
}
1;

上記のコードを使用してChildクラスを宣言するとuse parent 'Parent';、Parent コンストラクターは適切に子を構築します。

子にいくつかのプロパティを追加する必要がある場合、あなたが持っていたものはほとんど正しいです:

package Child;
use strict;
use warnings;

use parent 'Parent';

sub new
{
    my ($class, @args) = @_;

    # possibly call Parent->new(@args) first
    my $self = $class->SUPER::new(@args);

    # do something else with @args

    # no need to rebless $self, if the Parent already blessed properly
    return $self;
}
1;

ただし、複数の継承を混在させる場合、すべての段階で適切な処理を決定する必要があります。これは、Parent1 と Parent2 のプロパティを子にマージする方法を決定し、最終的に結果のオブジェクトを Child クラスに祝福する、すべてのクラスのカスタム コンストラクタを意味します。この複雑さは、多重継承が不適切な設計選択である多くの理由の 1 つです。おそらくいくつかのプロパティをロールに移動することによって、オブジェクト階層を再設計することを検討しましたか? さらに、Mooseなどの忙しい作業の一部を取り除くために、オブジェクト フレームワークを採用することもできます。現在、カスタム コンストラクターを記述する必要はほとんどありません。

$a(最後に、変数との使用は避けるべき$bです。これらは、並べ替え関数やその他の組み込み関数で使用される変数であるため、Perl では異なる方法で扱われます。)

于 2011-03-09T18:00:08.900 に答える
22

newこの問題が、メソッド で興味深いことを何もしないことを推奨する人がいる理由です。new祝福された参照を作成して返すことが期待されているため、異なる親クラスからの同じオブジェクトに対してこれを 2 回行うことを処理するシステムを作成するのは困難です。

よりクリーンなオプションは、オブジェクトを作成するだけで、オブジェクトをセットアップできる別のメソッドを呼び出す新しいメソッドを持つことです。この 2 番目のメソッドは、複数の親メソッドを呼び出すことができるように動作できます。事実new上アロケーターであり、この他のメソッドはコンストラクターです。

package Mother;
use strict;
use warnings;

sub new {
    my ($class, @args) = @_;
    my $self = bless {}, $class;
    return $self->_init(@args);
}

sub _init {
    my ($self, @args) = @_;

    # do something

    return $self;
}

package Father;
use strict;
use warnings;

sub new {
    my ($class, @args) = @_;
    my $self = bless {}, $class;
    return $self->_init(@args);
}

sub _init {
    my ($self, @args) = @_;

    # do something else

    return $self;
}

package Child;
use strict;
use warnings;

use base qw(Mother Father);

sub _init {
    my ($self, @args) = @_;

    # do any thing that needs to be done before calling base classes

    $self->Mother::_init(@args); # Call Mother::_init explicitly, SUPER::_init would also call Mother::_init
    $self->Father::_init(@args); # Call Father::_init explicitly, SUPER::_init would NOT call Father::_init

    # do any thing that needs to be done after calling base classes

    return $self;
}

Ether は、多重継承を使用することで発見される可能性が高い複雑さについては正しいです。Father::_initが最初に行った決定を上書きしないことを知っておく必要がありますMother::_init。そこからデバッグするのは、より複雑で難しくなるだけです。

私はMooseの推奨を支持します。そのインターフェースには、通常は機能する上記の例よりもオブジェクトの作成と初期化がより適切に分離されています。

于 2011-03-09T19:49:57.030 に答える
6

多重継承を使用する場合、デフォルトのメソッド解決順序はサブパーです。追加することを強くお勧めします

use mro 'c3';

モジュールに追加し、チェーン内の次のコンストラクターを呼び出すことを使用して

sub new {
   my ($class) = @_;
   return $class->next::method(@_);
}

これが機能するには、アルファ版とベータ版が同じことを行う必要があります。

参照: mro

于 2011-03-09T20:12:30.070 に答える