モダンなクラス/オブジェクトのあり方は?
Perlではそもそもオブジェクトという考え方自体が、
ただし、
今回はそのようなクラス/
継承によるクラスの拡張
伝統的なbaseプラグマを使ってクラスを拡張する場合、
use strict;
use warnings;
package ClassA;
sub initialize { print "A"; }
package ClassB;
use base 'ClassA';
sub initialize { print "B"; }
package main;
ClassB->initialize; # B
ただし、
use strict;
use warnings;
package ClassA;
sub initialize { print "A"; }
package ClassB;
use base 'ClassA';
sub initialize { print "B"; shift->SUPER::initialize; }
package main;
ClassB->initialize; # BA
SUPERの問題点
ただ、
use strict;
use warnings;
package ClassA;
sub initialize { print "A"; }
package ClassB;
sub initialize { print "B"; }
package ClassC;
use base qw(ClassA ClassB);
sub initialize { print "C"; shift->SUPER::initialize; }
package main;
ClassC->initialize; # CA
本当はClassAのinitializeも、
もちろん継承の仕方を変えて、
そこで登場したのがNEXTというモジュールでした。
NEXTの登場
NEXTは、
use strict;
use warnings;
use NEXT;
package ClassA;
sub initialize { print "A"; shift->NEXT::initialize; }
package ClassB;
sub initialize { print "B"; shift->NEXT::initialize; }
package ClassC;
use base qw(ClassA ClassB);
sub initialize { print "C"; shift->NEXT::initialize; }
package main;
ClassC->initialize; # CAB
このように、
NEXTの問題点
ところが、
use strict;
use warnings;
use NEXT;
package ClassA;
sub initialize { print "A"; shift->NEXT::initialize; }
package ClassB;
use base qw(ClassA);
sub initialize { print "B"; shift->NEXT::initialize; }
package ClassC;
use base qw(ClassA);
sub initialize { print "C"; shift->NEXT::initialize; }
package ClassD;
use base qw(ClassB ClassC);
sub initialize { print "D"; shift->NEXT::initialize; }
package main;
ClassD->initialize; # DBACA
このコードでもすでに一階層深いAがCより先に実行される
このような不安定さは、
Class::C3の登場
その努力は、
そのひとつがその数日後にリリースされたClass::C3でした。このClass::C3を使うと、
use strict;
use warnings;
use Class::C3;
package ClassA;
sub initialize { print "A"; }
package ClassB;
use base qw(ClassA);
sub initialize { print "B"; shift->next::method; }
package ClassC;
use base qw(ClassA);
sub initialize { print "C"; shift->next::method; }
package ClassD;
use base qw(ClassB ClassC);
sub initialize { print "D"; shift->next::method; }
package main;
ClassD->initialize; # DBCA
メソッドの解決順序が安定するClass::C3の登場は、
mroプラグマの導入
また、
use strict;
use warnings;
package ClassA;
use mro 'c3';
sub initialize { print "A"; }
package ClassB;
use base qw(ClassA);
use mro 'c3';
sub initialize { print "B"; shift->next::method; }
package ClassC;
use base qw(ClassA);
use mro 'c3';
sub initialize { print "C"; shift->next::method; }
package ClassD;
use base qw(ClassB ClassC);
use mro 'c3';
sub initialize { print "D"; shift->next::method; }
package main;
ClassD->initialize; # DBCA
ここではそれぞれのクラスの中で明示的にC3のアルゴリズムを利用するように指定しましたが、
互換性のためにMRO::Compatを
ただし、
そのため、
先の例であれば、
use strict;
use warnings;
use MRO::Compat;
package ClassA;
sub initialize { print "A"; }
package ClassB;
use base qw(ClassA);
sub initialize { print "B"; shift->next::method; }
package ClassC;
use base qw(ClassA);
sub initialize { print "C"; shift->next::method; }
package ClassD;
use base qw(ClassB ClassC);
sub initialize { print "D"; shift->next::method; }
package main;
ClassD->initialize; # DBCA
Class::C3::XSによる高速化
また、
MRO::Compat/Class::C3を使うときの注意点
よいこと尽くめに見えるC3探索ですが、
まず、
また、
複数のプラグイン、
sub foo {
my $self = shift;
my ($bar, $baz) = @_; # ←これが実際に引き回したい引数
$self->next::method(@_);
}
ただし、
sub foo {
my ($self, $bar, $baz) = @_;
next::method(@_);
}
NEXTと違って、
use strict;
use warnings;
use MRO::Compat;
package ClassA;
sub initialize { print "A"; maybe::next::method(@_); }
package ClassB;
use base 'ClassA';
sub initialize { print "B"; maybe::next::method(@_); }
package main;
ClassB->initialize;
本当に多重継承する必要はあるのでしょうか
さて、
次回はその命題にモダンPerlがどのように答えてきたかを見ていきましょう。