続・はじめてのPerl 11章
11章 オブジェクト入門
11.1 動物と話すことができたら
use strict; use warnings; sub Cow::speak { print "a Cow goes moooo!\n"; } sub Horse::speak { print "a Horse goes neigh!\n"; } sub Sheep::speak { print "a Sheep goes baaah!\n"; } my @pasture = qw/ Cow Cow Horse Sheep Sheep /; { no strict 'refs'; for my $beast ( @pasture ) { &{$beast . '::speak'}; # シンボリックコードレフ } }
シンボリックコードレフは読みにくい 他の方法はないのか?
11.2 メソッド呼び出しの矢印
Classとは 同じような動作と特性を持つものの集まり Classパッケージ内のmethodサブルーチンを呼び出す方法 Class->method メソッドとは サブルーチンのオブジェクト指向バージョン
# メソッド呼び出しの矢印を使う
Cow->speak;
Horse->speak;
Sheep->speak;
# さらに以下のように書ける my @pasture = qw/ Cow Cow Horse Sheep Sheep /; for my $beast ( @pasture ) { # シンボリックコードレフを使う必要がなくなった $beast->speak; }
OOPの原則 共通コードを最小限に減らすこと コーディング量の削減による時間の節約 テストとデバッグの効率化による時間の節約
11.3 メソッド呼び出しの特別な引数
矢印を使った、メソッド呼び出し Class->method( @args ); これは、以下のようにClass::method を呼び出すこととなる Class:method( 'Class', @args ); 第一引数として、クラス名を受け取る @argsが指定されていない場合、クラス名だけを引数として受け取る
# 第一引数を使うことで、以下のようにメソッドを定義できる use strict; use warnings; sub Cow::speak { my $class = shift; print "a $class goes moooo!\n"; } sub Horse::speak { my $class = shift; print "a $class goes neigh!\n"; } sub Sheep::speak { my $class = shift; print "a $class goes baaah!\n"; } my @pasture = qw/ Cow Cow Horse Sheep Sheep /; for my $beast ( @pasture ) { $beast->speak; }
11.4 第二のメソッドを呼び出して話を単純化する
前節のコードは、同じ構造のコードを多数含む どのように単純化するべきか
# use strict; { # Animalクラスを作成 # speakメソッドを定義する package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Cow; @ISA = qw/ Animal /; sub sound { 'moooo' } } Cow->speak;
Cow->speak; の処理の流れ 1. 引数リストを作る 上記の場合、Cowのみ 2. Cow::speakを探す しかし、Cowにはspeakメソッドはない 3. 継承配列の@Cow::ISAをチェックする @Cow::ISAは実在し、Animalという名前を持つ 4. Animalの中に、speakメソッド(Animal::speak)があるかチェックする speakを見つけることができる 5. 引数リストを使い、メソッドを呼び出す 以下のコードを実行することと同じことになる Animal::speak( 'Cow' ); 6. Animal::speakメソッド内で、$classにCowが格納される 7. $class->sound の呼び出しで、Cow->soundが呼び出される 8. "a Cow goes moooo!" という出力が得られる
11.5 @ISAについての注意
@ISA 「イズア」と発音 Cow is a Animal ということを宣言する 実際には、言語学用語 複数の親クラスを検索することがありえるので、配列 Animalも@ISAを持っている場合、それも検索対象 検索は、再帰的で深さ優先 @ISAの左の要素から右の要素へ検索が行われる 一般に@ISA配列の要素は1つだけ 多重継承がされている場合、複数になる use strictを有効にすると、@ISAについて警告される @ISAが、明示的なパッケージ名を含む変数でなく、レキシカル変数でもないため @ISAは、レキシカル変数にすることは不可能 継承メカニズムにより見つけられるパッケージに属していなければならない @ISAの宣言と設定方法 最も簡単な方法:パッケージ名を書く @Cow::ISA = qw/ Animal /; Perl5.6以降では、our宣言を使うことができる package Cow; our @ISA = qw/ Animal /; 暗黙に名前を与えたパッケージ変数として扱う package Cow; use vars qw/ @ISA /; @ISA = qw/ Animal /; これは、use base を使うことで以下のように書ける package Cow; use base qw/ Animal /; use baseはコンパイル時に実行される
11.6 メソッドのオーバーライド
use strict; { # Animalクラスを作成 # speakメソッドを定義する package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; print "[but you can barely hear it!]\n"; } } Mouse->speak;
Mouseは独自のspeakメソッドを持っている Animalクラスのspeakメソッドを呼び出すわけではない これをオーバーライドという オーバーライド 基底クラス(Animal)のメソッドの代わりとなる専用のメソッドを持っている場合に、 派生クラス(Mouse)のメソッドを呼び出すために使われる しかし、上記のMouse::speakは、Animal::speakのコードと同じ箇所がある Animal::speakに変更が発生した場合、 Mouse::speakにも手を加えなければならない可能性がある OOPの原則 コードの再利用はコピペではなく、継承で行う
# よりOOPらしいコードに use strict; { package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; Animal::speak( $class ); # 矢印によるメソッド呼び出しではない!! print "[but you can barely hear it!]\n"; } } Mouse->speak;
矢印によるメソッド呼び出しを使わない理由 矢印を使うと、Animal::speakの第一引数は、"Animal"になってしまう soundメソッドを呼び出すときに、オブジェクトに合ったメソッドが呼び出せない しかし、Animal::speakを直接呼び出すと、また別の問題が起こる 矢印によるメソッド呼び出しを使わなかった場合に起こる問題 継承関係の解決が行われない 呼び出そうとしたメソッドが継承されたメソッドだった場合、プログラムは異常終了する よって、上記のコードの方法は正しくないやり方
11.7 異なる場所からの検索の開始
use strict; { package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; $class->Animal::speak( @_ ); print "[but you can barely hear it!]\n"; } } Mouse->speak;
上記のコードは正しく動作する Animalでspeakを探す 見つからない場合は、Animalの継承チェーンをたどりspeakを探す 第一引数は、$classとなる この方法の問題点 @ISAと最初に検索するパッケージを同じものにする手作業が入る @ISAに複数のエントリがある場合、どちらがspeakを定義しているのか不明な場合がある
11.8 SUPERなやり方
use strict; { package Animal; sub speak { my $class = shift; print "a $class goes ", $class->sound, "!\n"; } } { package Mouse; use base qw/ Animal /; sub sound { 'squeak' } sub speak { my $class = shift; $class->SUPER::speak( @_ ); # SUPERクラスを使う print "[but you can barely hear it!]\n"; } } Mouse->speak;
Animalクラスを、SUPERクラスに書き換えることで、 @INCにエントリーされているクラスから、speakが検索されるようになる @INCの中で複数のspeakが定義されている場合 最初に見つかったものが呼び出される
11.9 @_の処理
親クラスの動作に何か処理を追加している場合 @_を引数として渡すべき $class->SUPER::speak( @_ ); 親クラスの動作を正確に制御するような場合 明示的に引数リストを見定めて、渡すべき
- 作者: Randal L. Schwartz,brian d foy,Tom Phoenix,吉川英興,伊藤直也,田中慎司,株式会社ロングテール/長尾高弘
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2006/10/21
- メディア: 大型本
- 購入: 9人 クリック: 389回
- この商品を含むブログ (99件) を見る