Вызов базового конструктора в 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;
Ответы
Ответ 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