Вызов базового конструктора в Perl - PullRequest
20 голосов
/ 09 марта 2011

Как правильно вызвать базовый конструктор из конструктора класса в 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;

Ответы [ 3 ]

33 голосов
/ 09 марта 2011

Если все, что делает ваш конструктор, это вызывает родительский конструктор (как в вашем примере, вам вообще не нужно его писать. Просто оставьте его и родительский вызов будет вызван; вам просто нужно убедиться, что объект благословен на правильный тип:

package Parent;
use strict;
use warnings;

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

    # do something with @args

    return bless {}, $class;
}
1;

Если вы используете приведенный выше код и класс Child объявлен с use parent 'Parent';, то конструктор Parent будет правильно создавать дочерний.

Если вам нужно добавить некоторые свойства в Child, то, что у вас было, в основном правильно:

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 в дочерний, а затем, наконец, благословляет получившийся объект в дочерний класс. Это осложнение является одной из многих причин, почему множественное наследование является плохим выбором дизайна. Рассматривали ли вы перестройку иерархии объектов, возможно, путем перемещения некоторых свойств в роли? Кроме того, вы можете использовать объектную инфраструктуру для выполнения некоторой занятой работы, например Moose . В настоящее время редко требуется написать собственный конструктор.

(Наконец, вам следует избегать использования переменных $a и $b; они обрабатываются по-разному в Perl, поскольку они являются переменными, используемыми в функциях сортировки и некоторых других встроенных модулях.)

20 голосов
/ 09 марта 2011

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

Более чистая опция - иметь новый метод, который только создает объект и вызывает другой метод, который может настроить объект. Этот второй метод может вести себя так, что позволяет вызывать несколько родительских методов. Фактически 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;
}

Эфир прав насчет осложнений, которые вы, вероятно, найдете при использовании множественного наследования. Вам все еще нужно знать, что Father::_init не будет отменять любые решения, принятые Mother::_init для начала. От этого будет только сложнее и сложнее отлаживать.

Я бы поддержал рекомендацию Moose , его интерфейс включает в себя лучшее разделение создания и инициализации объекта, чем мой пример выше, который обычно просто работает.

5 голосов
/ 09 марта 2011

При использовании множественного наследования порядок разрешения методов по умолчанию - подпаритет.Я настоятельно рекомендую вам добавить

use mro 'c3';

в модули и вызвать следующий конструктор в цепочке, используя

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

. Альфа и Бета должны сделать то же самое дляработа.

Ссылка: мро

...