PERLOBJ(1) USER COMMANDS PERLOBJ(1) NAME perlobj - Perl のオブジェクト DESCRIPTION まず最初に、Perl でのリファレンスが何かを理解する必要があり ます。 そのため、perlref manpage を参照してください。 ここに、安心材料としてとても簡潔な定義を 3 つ示します。 1. オブジェクトとは、単に自分が属するクラスを知っているリフ ァレンスのことです。 2. クラスとは、単にオブジェクトリファレンスを扱うためのメソ ッドを提供するパッケージのことです。 3. メソッドとは、オブジェクトリファレンス (あるいは、静的メ ソッドには、パッケージ名) を最初の引数として期待するサブ ルーティンのことです。 ここでは、これらのポイントを掘り下げていきます。 オブジェクトは単にリファレンスである C++ とは違って、Perl ではコンストラクタのために、特別な構文 を用意していません。 コンストラクタは単に、あるクラス内に "bless" されたリファレンスを返すサブルーティンで、一般にその クラスは、該当サブルーティンが定義されているクラスです。 典 型的なクラスの例を示します: package Critter; sub new { bless {} } {} は、key/value ペアを 1 つも含まず、名前の無いハッシュへの リファレンスを作ります。 bless() がそのリファレンスを受け取 って、そのリファレンスが参照するオブジェクトに対し、現在のパ ッケージが Critter であることを告げ、そのリファレンスを返し ます。 これは、参照されたオブジェクト自身が bless されたこ とを知っているため、簡単にしたもので、以下のようにそのリファ レンスを直接返することができあます: sub new { my $self = {}; bless $self; return $self; } 実際には、コンストラクタによる構築の一部として、クラス内のメ ソッドを呼ぶような、より込み入ったコンストラクタで、このよう な構成を目にすることが多くなります。 Perl manpages Last change: Release 5.0 Patchlevel 00 1 PERLOBJ(1) USER COMMANDS PERLOBJ(1) sub new { my $self = {} bless $self; $self->initialize(); $self; } クラスパッケージ内では、メソッドは、そのリファレンスを通常の リファレンスとして扱います。 クラスパッケージの外では、その リファレンスは一般に、そのクラスのメソッドを通してのみアクセ スできる、直接参照できない値として扱われます。 コンストラクタは、その時点で別のクラスに属していて参照されて いるオブジェクトを、再度 bless することができますが、その場 合には、新しいクラスが、最後の後片付けに全責任を持ちます。 以前に行なわれた bless は忘れ去られ、オブジェクトは、その時 点では、1 つのクラスにしか属することができません。 (ですが、 もちろん多くのクラスからメソッドを継承することは、自由です。) 明確化事項: Perl オブジェクトは、bless されます。 単なるリ ファレンスは、されません。 オブジェクトは、自分が属している パッケージがどれであるかを知っています。 リファレンスは、知 りません。 bless() 関数は、オブジェクトを探すために、単にリ ファレンスを用います。 以下の例を見てみましょう: $a = {}; $b = $a; bless $a, BLAH; print "\$b is a ", ref($b), "\n"; このプログラムは、$b が BLASH であると報告してくれます。 つ まり、明らかに bless() は、オブジェクトに対して働くのであっ て、リファレンスに働くものではありません。 クラスとは単にパッケージである C++ などとは違って、Perl ではクラス定義のための特別な構文を 用意していません。 メソッドの定義を入れることによって、パッ ケージをクラスとして使用します。 各々のパッケージには、@ISA という特別な配列があり、現在のパ ッケージでメソッドが見つからなかった場合に、どこを探すかとい うことが書いてあります。 Perl では、これによって継承を実現 しています。 配列 @ISA の各要素は、たまたまクラスである、他 のパッケージの名前になっています。 クラスは、@ISA に現われ る順序で、見つからなかったメソッドを (深度優先) 検索します。 @ISA を通じてアクセスできるクラスをカレントクラスの基底クラ スといいます。 必要なメソッドが、基底クラスの中に見つかれば、効率化のために、 現在のクラスの中にキャッシュされます。 @ISA を変更したり、 Perl manpages Last change: Release 5.0 Patchlevel 00 2 PERLOBJ(1) USER COMMANDS PERLOBJ(1) 新しいサブルーティンを定義すると、そのキャッシュを無効にし、 再び Perl に検索させるようにします。 メソッドが見つからず、AUTOLOAD ルーティンが見つかれば、見つ からなかったメソッドの代わりに、それが呼び出されます。 メソッドも AUTOLOAD ルーティンも @ISA に見つからなければ、最 後の手段として、UNIVERSAL というクラスで、メソッド (あるいは AUTOLOAD ルーティン) を探します。 これでもダメならば、最終 的に諦めて、エラーを出すことになります。 Perl のクラスは、メソッドの継承だけを行ないます。 データの 継承は、クラス自身に任されます。 全般的にこのことは、Perl では問題とはなりません。 多くのクラスは、自分のオブジェクト の属性を、名無しのハッシュを使ってモデル化するからです。 そ のハッシュは、そのオブジェクトと何らかの関係を持とうとする、 さまざまなクラスによって切り分けられるために、自分自身の小規 模な名前空間として働きます。 メソッドとは単にサブルーティンである C++ などとは違って、Perl ではメソッド定義のための特別な構文 を用意していません。 (だたし、メソッド呼び出しのためには、 少しばかり構文を用意しています。 これについては後程。) メ ソッドでは、最初の引数がオブジェクトか、自分が呼び出されたパ ッケージである必要があります。 メソッドには、2 つの種類があ り、C++ での最も近い 2 つのメソッドの種類に準じて、静的メソ ッドと仮想メソッドと呼びます。 静的メソッドは、最初の引数としてクラス名を期待します。 これ は、全体としてのクラスに対する機能を果たすもので、クラスに属 する個々のオブジェクトに対するものではありません。 コンスト ラクタは、典型的な静的メソッドです。 多くの静的メソッドは、 その第一引数を単に無視してしまいます。 既にどのパッケージに いるのかは、分かっていますし、どのパッケージを介して起動され たかは、関係がないからです。 (静的メソッドは、通常の仮想メ ソッドと同じように、継承トリーをたどっていますから、これらの パッケージが等しいとは限りません。) この他に、静的メソッド を使う典型例としては、名前によるオブジェクトの検索があげられ ます: sub find { my ($class, $name) = @_; $objtable{$name}; } 仮想メソッドは、最初の引数としてオブジェクトリファレンスを期 待します。 最初の引数を shift して、"self" や "this" という 変数にいれ、通常のリファレンスとして使うことがよく行なわれま す。 Perl manpages Last change: Release 5.0 Patchlevel 00 3 PERLOBJ(1) USER COMMANDS PERLOBJ(1) sub display { my $self = shift; my @keys = @_ ? @_ : sort keys %$self; foreach $key (@keys) { print "\t$key => $self->{$key}\n"; } } メソッド呼び出し メソッドを呼び出す方法には 2 つあり、その 1 つは既になじみの 深いものですし、もう 1 つは見たことのある格好をしています。 Perl 4 は、既に print STDERR "help!!!\n"; としたときに使われる、「間接オブジェクト」構文を持っていまし た。 この同じ構文が、静的メソッドにも仮想メソッドにも使われます。 先に定義した 2 つのメソッド、オブジェクトリファレンスを検索 するための静的メソッド、その属性を出力するための仮想メソッド を使ってみましょう。 $fred = find Critter "Fred"; display $fred 'Height', 'Weight'; これらは、間接オブジェクトのところに BLOCK を使って、1 つの 文に組み合わせることができます: display {find Critter "Fred"} 'Height', 'Weight'; C++ ファンのために、同じことをする -> を使った構文も用意して あります。 引数がある場合には、括弧が必要です。 $fred = Critter->find("Fred"); $fred->display('Height', 'Weight'); あるいは、1 文で、 Critter->find("Fred")->display('Height', 'Weight'); ある構文が読みやすいと感じることがあり、別の構文の方が読みや すいと感じることもあります。 間接オブジェクトの構文は、混乱 させるようなものではありませんが、通常のリスト演算子としての、 同様の曖昧さも持っています。 間接オブジェクトのメソッド呼び 出しは、リスト演算子と同じ規則を使って解釈されます: 「関数の ように見えれば、それは関数です。」 (2 つの並んだ単語が関数 名のように見えると考えれば、です。 C++ のプログラマは、特に 最初の単語が "new" のときに、規則的にそのように考えるようで す。) つまり、 Perl manpages Last change: Release 5.0 Patchlevel 00 4 PERLOBJ(1) USER COMMANDS PERLOBJ(1) new Critter ('Barney', 1.5, 70) という括弧は、その後に何が来ようとも、メソッドに対するすべて の引数を囲っているものとして扱われます。 new Critter ('Bam' x 2), 1.4, 45 と書くのは、 Critter->new('Bam' x 2), 1.4, 45 と同じことで、目的とは違うでしょう。 どのクラスのメソッドを使うかを指定したい場合もあります。 そ の場合には、通常のサブルーティン呼び出しとして、メソッドを呼 び出すことができます。 必須の最初の引数は、明示的に渡さなけ ればなりません: $fred = MyCritter::find("Critter", "Fred"); MyCritter::display($fred, 'Height', 'Weight'); しかしながら、この場合には継承を行ないません。 単に Perl が メソッドの検索を開始する特定のパッケージを指定したいだけなら ば、メソッド名にパッケージ名を付けて、通常のメソッド呼び出し を使ってください: $fred = Critter->MyCritter::find("Fred"); $fred->MyCritter::display('Height', 'Weight'); デストラクタ オブジェクトに対する最終的な参照がなくなると、オブジェクトは 自動的に、削除されます。 (これは、グローバル変数にリファレ ンスを入れている場合には、exit した後になるかもしれません。) オブジェクトがなくなる直前に制御をもらいたい場合には、クラス 内で、DESTROY メソッドを定義することができます。 これは、適 切なときに自動的に呼び出されますから、その中で独自の後片付け を行なうことができます。 Perl では、ネストした削除を行ないません。 コンストラクタで、 基底クラスの一つからリファレンスを再 bless したような場合に は、DESTORY のなかで、必要な基底クラスの DESTROY を呼び出す 必要があるかもしれません。 しかし、これは再 bless されたオ ブジェクトにだけあてはまります。 現在のオブジェクトに含まれ ているだけのオブジェクトリファレンスは、その現在のオブジェク トが解放されるときに、自動的に解放され、削除されます。 Perl manpages Last change: Release 5.0 Patchlevel 00 5 PERLOBJ(1) USER COMMANDS PERLOBJ(1) まとめ これで、ほとんどすべてです。 さて、それでは、すぐにも出かけ て、オブジェクト指向設計の方法論に関する本を仕入れて、今から 半年くらいは、それに頭を打ちつけ続けることが必要でしょう。 SEE ALSO その他のオブジェクトのしくみ、わな、助言について、perlbot manpage もチェックしておいてください。 Perl manpages Last change: Release 5.0 Patchlevel 00 6