Perl Perl_6 オブジェクト指向プログラミング
Perl OOP 継承 オーバーライド SUPER:: (d081)

目次 - Perl Index
Theme
Perl について、復習を兼ねて断片的な情報を掲載して行く連載その d081 回。
Perl での「 オブジェクト指向プログラミング 」( Object-Oriented Programming : OOP ) で、継承したクラスでデストラクタ「 DESTROY 」を利用する際の注意点を確認する前に、擬似クラスと呼ばれる修飾子「 SUPER:: 」を確認する。
継承
(d069) では、クラスの継承 ( inheritance ) の基本について確認しました。
Perl の OOP では継承のためにいくつかの方法が用意されています。それは例えば次のようなものです。
# @ISA で SomeParent を継承
package SomeChild;
our @ISA = qw( SomeParent );
...
# parent で SomeParent を継承
package SomeChild;
use parent = qw( SomeParent );
...
モジュール「 parent 」はモジュール「 base 」からフォークしたよりシンプルなモジュールで、そのどちらも内部的には「 @ISA 」を使っています。
組み込みの特別な変数「 @ISA 」は、親クラスとの「 ISA 」関係をもったクラスを保持するためのものなで、上記記述によって、子クラス「 SomeChild 」は親クラス「 SomeParent 」と「 SomeChild is a SomeParent 」の関係になります。
オーバーライド
OOP の基礎知識 (d067) で少し確認した「 オーバーライド 」( override ) は、継承した親クラスのメソッドを上書き ( 親のメソッドよりも優先して実行 ) する機能です。
例として、「 続・初めての Perl 改訂版 」でお馴染みのクラス「 Animal 」とこれを継承するクラス「 Horse 」を考えます。
次のコードでは、子クラス「 Horse 」が親クラス「 Animal 」のメソッド「 sound() 」と「 speak() 」をオーバーライドするようにしてあります。コンストラクタ「 new() 」と名前へのアクセサ「 name() 」は親クラスから拝借してそのまま使います。
# 親になるクラス
package Animal;
# コンストラクタ
sub new {
my $class = shift;
my $name = shift || 'Tarou';
my $self = { Name => $name };
bless $self, $class;
}
# 名前のアクセサ
sub name {
my $either = shift;
ref $either ? $either->{Name} : "an unnamed $either";
}
# 鳴き声のメソッド
sub sound { 'hummm...' }
# 鳴くメソッド
sub speak {
my $either = shift;
print 'In the Animal ', $either->name, ' goes ', $either->sound, "\n";
}
# 子になるクラス
package Horse;
# Animal を継承する ( Horse is a Animal )
our @ISA = qw(Animal);
# Animal の sound をオーバーライド
sub sound { 'neigh' }
# Animal の speak をオーバーライド
sub speak {
my $self = shift;
print 'In the Horse! ',$self->name, ' goes ', $self->sound, "\n";
}
このコードを次のように使えば、親子それぞれのメソッドを利用できます。
# in main
# 親子それぞれのインスタンスをコンストラクト
my $animal = Animal->new('Parent');
my $horse = Horse->new('Child');
# 親クラスの speak()
print "Animals speak: ";
$animal->speak;
# 子クラスの speak()
print "Horses speak : ";
$horse->speak;
__END__
# output
Animals speak: In the Animal! Parent goes hummm...
Horses speak : In the Horse! Child goes neigh
親のメソッドを SUPER:: で引き継ぐ
前項のオーバーライドでは、メソッドの機能を損なうことなく利用できていますが、子クラスでは親クラスのメソッドをまるごと上書きしているので、もしそこに重複した処理があるなら、その重複した部分を親子どちらのメソッドにも記述しなければなりません。
メソッド「 speak 」の記述を見てみます。
# in Animal
sub speak {
my $either = shift;
print 'In the Animal! ', $either->name, ' goes ', $either->sound, "\n";
}
# in Horse
sub speak {
my $self = shift;
print 'In the Horse! ',$self->name, ' goes ', $self->sound, "\n";
}
ほとんど同じ作業をしています。
こうした重複を削減するには、(d067) で少し確認した「 SUPER:: 」が使えます。これはメソッド名を修飾するための修飾子で「 擬似クラス 」( pseudo-class ) と呼ばれます。
この修飾子を使うことで、「 @ISA 」に登録された親クラス、つまり別名「 スーパークラス 」( super class ) から指定したメソッドを直接呼び出すことができます。
# e.g. 親クラスのメソッド method を呼び出す
$self->SUPER::method();
これ ( と関数 ref ) を使えば、重複した箇所を次のように変更できます。
# in Animal
sub speak {
my $either = shift;
# 関数 ref でクラス名を取得
print "In the ", ref $either, "! ", $either->name, ' goes ', $either->sound, "\n";
}
# in Horse
sub speak {
my $self = shift;
# SUPER:: で Animal の speak を呼び出す
$self->SUPER::speak;
# Horse のみの追加メッセージ
print "adding message.\n";
}
これで、コードの重複を削減することができました。
そもそも同じ処理をするならメソッドのオーバーライドは不要ですが、修飾子「 SUPER:: 」を使えば、例えばコンストラクタ「 new 」をオーバーライドしつつ子クラス専用のアトリビュートを追加するなど、親クラスのメソッドに対する追加処理を簡単に実装できます。
なお、main プログラム側のコードはまったく書き換える必要はありません ( ポリモーフィズム ? )。
NEXT
次回は、クラスを継承する際のデストラクタの注意点を確認します。
参考情報は書籍「 続・初めての Perl 改訂版 」, 「 Effective Perl 第 2 版 」を中心に perldoc, Wikipedia および各 Web サイト。それと詳しい先輩。
目次 - Perl Index
Perl mp2 翻訳 Web コンテンツ圧縮の FAQ (d228)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)