2014-02-03 6 views
2

У меня есть класс Moo (se) [0] с рядом методов, которые имеют один и тот же тип «инструкции охраны» вверху. Вместо того, чтобы писать один и тот же код несколько раз, я решил, что могу поставить заявление в модификатор метода «до», и это работает отлично. Если этот класс не является подклассом, потому что тогда «перед защитой» никогда не вызывается.Moo (se), before и inheritance

package Foo; 
use feature 'say'; 
use Moose; 

has '_init' => (
    is  => 'rw', 
    isa  => 'Bool', 
    default => 0 
); 

sub init { 
    shift->_init(1); 
} 

sub method { 
    say "in Foo::method"; 
} 

before method => sub { 
    my $self = shift; 
    warn "==> Foo is not initialized\n" unless $self->_init; 
}; 


package Bar; 
use feature 'say'; 
use Moose; 
extends 'Foo'; 

sub method { 
    say "in Bar::method"; 
} 


package main; 
use feature 'say'; 

my $foo = Foo->new; 
say "foo the wrong way:"; 
$foo->method; 

say "foo the right way:"; 
$foo->init; 
$foo->method; 

my $bar = Bar->new; 
say "bar the wrong way:"; 
$bar->method; 

Выход затем (с некоторыми добавлениями новых линий):

foo the wrong way: 
==> Foo is not initialized 
in Foo::method 

foo the right way: 
in Foo::method 

bar the wrong way: 
in Bar::method 

Я предполагаю, что это поведение является, но есть ли (хороший) способ убедиться, что все подклассы также наследуют " перед "инструкцией модификатора метода/охраны? Или есть другой способ сделать это (я подозреваю, что это довольно распространенная конструкция). Обратите внимание, что исключение будет выведено в операторе real guard, но «предупреждение» намного проще в примере кода.

[0] Я предпочитаю использовать Moo, потому что я не использую никаких функций, требующих MOP, но оба Moo и Moose работают точно так же в этом вопросе.

Редактировать Использование ролей.

Если вы добавили Role для этого (как предложено tobyink), и добавьте еще один способ сделать вещи немного более «реальной жизнью», я получаю своеобразный результат.

package Warning::NotInit; 
use feature 'say'; 
use Moose::Role; 

has '_init' => (is => 'rw', isa => 'Bool', default => 0); 

before qw/ m1 m2/=> sub { 
    my $self = shift; 
    my $class = ref($self); 
    warn "==> $class is not initialized\n" unless $self->_init; 
}; 

package Foo; 
use feature 'say'; 
use Moose; 
with 'Warning::NotInit'; 

sub init { shift->_init(1) } 
sub m1 { say "in Foo::m1" } 
sub m2 { say "in Foo::m2" } 

package Bar; 
use feature 'say'; 
use Moose; 
extends 'Foo'; 
with 'Warning::NotInit'; 

sub m1 { say "in Bar::m1" } 


package main; 
use feature 'say'; 

При вызове не перекрытый метод в подклассе метод before называется дважды.

my $bar = Bar->new; 
say "bar the wrong way:"; 
$bar->m1; 
$bar->m2; 

Выход:

bar the wrong way: 
==> Bar is not initialized 
in Bar::m1 

==> Bar is not initialized 
==> Bar is not initialized 
in Foo::m2 

Почему она называется дважды?

+0

Кроме того, я скажу, что ваша общая цель, кажется, чтобы убедиться, конкретный метод никогда не вызывает вызов объекта в неполном/несогласованном состоянии. Лучшим решением было бы убедиться, что ваш объект никогда не находится в этом состоянии. Напишите элемент «BUILD», который гарантирует, что ваш объект будет помещен в полное/согласованное состояние, как только он будет создан, и убедитесь, что ваши методы никогда не возвращают его в неполное/несогласованное состояние. – tobyink

+0

В предыдущих итерациях кода я поместил весь установочный код в 'BUILD', но это привело к тому, что в подклассу все было сложно/беспорядочно. Конкретный класс представляет собой обертку Net :: Telnet с настройками для конкретной целевой платформы. Некоторым из подклассов необходимо настроить параметры в Net :: Telnet-> new (в родительском 'BUILD'), но по мере того, как подкласс' BUILD' вызывается после родителя, это было слишком поздно. – PerC

ответ

2

Да, это не так, как работают модификаторы метода. Модификатор before будет часть метод сам. Когда вы переопределяете метод в вашем подклассе, вы переопределяете все поведение суперкласса, то есть вы также переопределяете модификатор метода.

Вы могли бы решить эту проблему путем факторизации метода модификатора в роли, которая может быть применена к каждому классу, как это:

package Warning::NotInit; 
use feature 'say'; 
use Moose::Role; 

has '_init' => (
    is  => 'rw', 
    isa  => 'Bool', 
    default => 0 
); 

before method => sub { 
    my $self = shift; 
    my $class = ref($self); 
    warn "==> $class is not initialized\n" unless $self->_init; 
}; 

package Foo; 
use feature 'say'; 
use Moose; 
with 'Warning::NotInit'; 

sub init { 
    shift->_init(1); 
} 

sub method { 
    say "in Foo::method"; 
} 

package Bar; 
use feature 'say'; 
use Moose; 
extends 'Foo'; 
with 'Warning::NotInit'; 

sub method { 
    say "in Bar::method"; 
} 


package main; 
use feature 'say'; 

my $foo = Foo->new; 
say "foo the wrong way:"; 
$foo->method; 

say "foo the right way:"; 
$foo->init; 
$foo->method; 

my $bar = Bar->new; 
say "bar the wrong way:"; 
$bar->method; 
+0

Хм, используя «Роль», определенно умная идея. Единственное плохое, что происходит по этому маршруту, - это тесная связь между ролями и потребительскими классами, поскольку Роль должна точно знать, какие методы следует модифицировать.Но для частных классов это не будет большой проблемой, я думаю. – PerC

+0

Если вы используете параметризованную роль, вы можете передать ей список методов для изменения как часть инструкции 'with'. [MooseX :: Устаревший] (https://metacpan.org/pod/MooseX::Deprecated) делает что-то подобное. – tobyink