オブジェクト偏その9-C3継承

前回までの復習です。

  • Perlのオブジェクトは元々は変数のリファレンスです。
  • blessするとオブジェクトとして振る舞います。
  • その性質はblessで関連づけられたクラスの性質を引き継ぎます。
  • そのクラスは他のクラスの性質を引き継ぐことができます。
  • どのクラスの性質を引き継いでいるかは@ISAという配列で管理しています。
  • blessで関連付けられたクラスにメソッドがない場合は@ISAにあるクラスにそのメソッドがないか調べます。-@ISAに複数のクラスが指定されている場合、最初に発見されたメソッドを使います。
  • 最初に発見されたメソッドが親クラスの処理を委譲されている場合はSUPERを使います。
  • 委譲するというのは処理を完全に上書きではなく、処理の前後に別の処理を加えているケースです。
  • 属性の初期化など、似たような処理を同名のメソッドで行うために、Initialiazableクラスを定義しました。
  • 全てのクラスはこのクラスを継承しているという約束事が必要です。
  • Initialiazableで定義されているinit()メソッドを各クラスが順番に行う仕組みが必要です。
  • @ISAに格納されている順番にその処理を行う実装を行えました。
  • さらにNEXT.pmというコアモジュールを使えばもっと実装が楽になりました。

ダイヤモンド継承

次の継承パターンで、@ISAに格納されているクラスを見てみましょう。


次のスクリプトを記述して、実行してみてください。
C:\work\exaple\oop_009.pl

#!C:\Perl\bin\perl
package main;
use strict;
use warnings;
use NEXT;

ClassD->based_on;
ClassD->order;

package ClassA;
use strict;
use warnings;
use vars qw(@ISA);
sub based_on { print "ClassA is based on ", join ', ', @ISA ,"\n"; shift->NEXT::based_on; }
sub order { print "A" ; shift->NEXT::order };

package ClassB;
use strict;
use warnings;
use vars qw(@ISA);
use base qw(ClassA);
sub based_on { print "ClassB is based on ", join ', ', @ISA ,"\n"; shift->NEXT::based_on; }
sub order { print "B" ; shift->NEXT::order };


package ClassC;
use strict;
use warnings;
use vars qw(@ISA);
use base qw(ClassA);
sub based_on { print "ClassC is based on ", join ', ', @ISA ,"\n"; shift->NEXT::based_on; }
sub order { print "C" ; shift->NEXT::order };

package ClassD;
use strict;
use warnings;
use vars qw(@ISA);
use base qw(ClassB ClassC);
sub based_on { print "ClassD is based on ", join ', ', @ISA ,"\n"; shift->NEXT::based_on; }
sub order { print "D" ; shift->NEXT::order };

実行してみましょう。


ClassAが2回呼び出されています。これは@ISAの性質を考えれば実に正しい動作なのですが、クラス設計者には好ましくない挙動です。

Class::C3ではなくやっぱり「use mro;」

じゃあ、クラス自体にどのクラスを呼び出したかを実装すればよいのかというと、まぁそうです。イニシャライズの際に一度呼び出されているかどうかを記録して、、、あ、デストラクタも2度呼び出されないように実装を、、、

そんなことしなくてもClass::C3が全てを解決してくれます。Perl5.10からは「use mro;」を使えばClass::C3は使用しなくてもよくなりました。

次のスクリプトを記述して、実行してみてください。
C:\work\exaple\oop_009_002.pl

※メソッド名をmethodと書き直してください。

#!C:\Perl\bin\perl
package main;
use strict;
use warnings;

ClassD->based_on;
ClassD->order;

package ClassA;
use strict;
use warnings;
use vars qw(@ISA);
use mro 'c3';
sub based_on { print "ClassA is based on ", join ', ', @ISA ,"\n"; }
sub order { print "A" ;  };

package ClassB;
use strict;
use warnings;
use vars qw(@ISA);
use base qw(ClassA);
use mro 'c3';
sub based_on { print "ClassB is based on ", join ', ', @ISA ,"\n"; shift->next::method; }
sub order { print "B" ; shift->next::method };


package ClassC;
use strict;
use warnings;
use vars qw(@ISA);
use base qw(ClassA);
use mro 'c3';
sub based_on { print "ClassC is based on ", join ', ', @ISA ,"\n"; shift->next::method; }
sub order { print "C" ; shift->next::method };

package ClassD;
use strict;
use warnings;
use vars qw(@ISA);
use base qw(ClassB ClassC);
use mro 'c3';
sub based_on { print "ClassD is based on ", join ', ', @ISA ,"\n"; shift->next::method; }
sub order { print "D" ; shift->next::method };


ClassAが一度しか呼び出されていません。これならクラス設計時に継承をうまくコントロールできそうです。

お疲れさまでした。
おざなりな説明になりましたが、これまで説明してきた内容でだいぶ再利用ができるようになっている、といいですね。

次回はデストラクションについて解説したいと思います。