Подтвердить что ты не робот

Вызов базового конструктора в perl

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

Ответ 1

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

Ответ 2

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

package Parent;
use strict;
use warnings;

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

    # do something with @args

    return bless {}, $class;
}
1;

Если вы используете приведенный выше код и имеете класс Child, объявленный с помощью use 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;

Однако, когда вы добавляете множественное наследование в микс, вам нужно решить, что нужно делать на каждом шагу. Это означает, что пользовательский конструктор для каждого класса, который решает, как объединить свойства родителя1 и родителя2 в дочерний элемент, а затем, наконец, благословляет результирующий объект в классе Child. Это осложнение является одной из многих причин, по которым множественное наследование является плохим выбором дизайна. Рассматривали ли вы перепроектирование своей иерархии объектов, возможно, переместив некоторые свойства в роли? Кроме того, вы можете использовать инфраструктуру объекта, чтобы вытащить часть занятой работы, например Moose. В настоящее время редко приходится писать пользовательский конструктор.

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

Ответ 3

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

use mro 'c3';

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

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

Alpha и Beta должны были бы сделать то же самое для работы.

Ссылка: mro