Perl 5 では、オブジェクト指向の概念が取り入れられ、クラスに基づいたプログラミングが可能になりました。オブジェクト指向では、次の用語が用いられます。
用語 | 説明 |
---|---|
クラス | オブジェクトの型。 |
メソッド | クラスが持つ機能。メンバ関数 とも呼ばれます。 |
アトリビュート | クラスが持つ値。属性 とか、メンバ変数 とか、プロパティ とも呼ばれます。 |
オブジェクト | 実体。インスタンス とも呼ばれます。 |
例えば、ロボットを示す Robot というクラスを考えます。
use Robot; $r1 = Robot->new(); # ロボットを1台定義する $r1->{Name} = "Taro"; # ロボットに Taro という名前をつける $r1->go(); # ロボットに「進め」と命令する $r1->back(); # ロボットに「戻れ」と命令する $r2 = Robot->new(); # ロボットをもう1台定義する $r2->{Name} = "Hanako"; # ロボットに Hanako という名前をつける $r2->go(); # ロボットに「進め」と命令する
オブジェクト指向の各用語と上記のプログラムの対応は次のようになります。
分類 | 例 |
---|---|
クラス名 | Robot |
メソッド | go()、back() |
アトリビュート | Name |
オブジェクト | $r1、$r2 |
このように、メソッドやアトリビュートをクラスの中に包含し、プログラム中の変数をひとつの「もの」として扱うことにより、プログラム効率を高めようというのがオブジェクト指向の考え方です。
モジュールでクラスを定義する例を下記に示します。
package Person1; # クラス変数 $count = 0; # コンストラクタ(生成関数) sub new { my($class, $name) = @_; # 第1引数はクラス名 my($self) = {}; # 無名参照オブジェクトを生成 bless($self, $class); # オブジェクトとクラス名を関連付け $self->{NAME} = $name; # 属性を初期化 $count++; # クラス変数を更新 return $self; # オブジェクト(無名参照)を返す } # デストラクタ(消滅関数) sub DESTROY { my($self) = @_; # 第1引数はオブジェクト $count--; # クラス変数を更新 } # メソッド:オブジェクトの総数を返す sub getCount { return $count; } # メソッド:NAME属性を設定する sub setName { my($self, $name) = @_; $self->{NAME} = $name; } # メソッド:NAME属性を参照する sub getName { my($self) = @_; return $self->{NAME}; } 1; # use がエラーにならないように 0 以外の値を記述しておく
詳細や使用方法は次ページ以降で説明します。
オブジェクトが生成されるときに暗黙的に呼び出されるサブルーチンを コンストラクタ(生成関数)と呼び、メンバ変数の初期化処理などを行います。通常、new() という名前で作成し、オブジェクト作成時に下記のようにして呼び出します。
use Person1; # Person1.pm 参照 $p1 = Person1->new("田中");
コンストラクタの中では、bless() を用いてオブジェクト(無名参照オブジェクト)とクラス名の結び付けを行っています。コンストラクタを呼び出す側の第1、第2、第3引数が、コンストラクタ new() の第2、第3、第4引数になります。new() の第1引数にはクラス名が渡されます。言いかえると、Person1->new(args, ...) は、Person1::new("Person1", args, ...) と同じ意味を持ちます。
デストラクタ(消滅関数)は、変数がスコープの範囲を超えたり、undef によって消滅する際に自動的に呼び出されるサブルーチンです。DESTROY という決まった名前を持ちます。不要なメモリ領域を開放したり、ファイルをクローズしたりなどの後処理をデストラクタとして定義しておくと、オブジェクトが消滅する際に自動的に後処理が行われるので便利です。
use Person1; # Person1.pm 参照 func(); sub func() { my($p1) = Person1->new(); # スコープ範囲を超えるため、 # この時点で $p1 のデストラクタが自動的に呼ばれる } $p2 = Person1->new(); undef $p2; # この時点で $p2 のデストラクタが自動的に呼ばれる
オブジェクト固有の値を アトリビュート と呼びます。プロパティ、メンバ変数、属性 などとも呼ばれます。オブジェクトの実体である、無名参照オブジェクトの連想配列データとして実装されます。下記の例では、コンストラクタ呼び出し時にアトリビュート Name の値を初期化しておき、$p1->{'NAME'} でその値を参照しています。
use Person1; # Person1.pm 参照 $p1 = Person1->new("田中"); print "名前:" . $p1->{'NAME'} . "\n";
上記のように、アトリビュートを直接参照することもできますが、下記のように getName() のようなメソッドでアトリビュートを隠蔽することも多いようです。
print "名前:" . $p1->getName() . "\n";
クラスに固有のサブルーチンを メソッド と呼びます。メンバ関数 とも呼ばれます。Person1.pm では、getCount()、getName()、setName() の3つのメソッドが定義されています。
use Person1; # Person1.pm 参照 $p1 = Person1->new("田中"); $p1->setName("鈴木"); print $p1->getName() . "\n";
Person1->new("田中") が Person1::new("Person1", "田中") と同義であるのと同様、$p1->setName("鈴木") は Person1::setName($p1, "鈴木") と同じ意味を持ちます。
クラス変数 は、個々のオブジェクトではなく、個々のクラスが持つ変数です。そのクラスのオブジェクトの個数などを管理する場合などに用います。Person1.pm では、$count がクラス変数に相当します。最初に 0 を設定しておいて、コンストラクタでひとつ加算、デストラクタでひとつ減算、メソッド getCount() でその値を取得しています。
use Person1; # Person1.pm 参照 $p1 = Person1->new("田中"); $p2 = Person1->new("鈴木"); $p3 = Person1->new("山田"); undef $p2; print "現在オブジェクトが" . Person1->getCount() . "個あります。\n";
オブジェクト指向の特徴のひとつに、クラスの継承 があります。すでに定義されているクラスに機能を追加して新しいクラスを定義するための仕組みです。
package Person2; # Person1のサブクラスとして定義する use Person1; # Person1.pm 参照 @ISA = qw(Person1); # 親クラスのリスト # コンストラクタ sub new { my($class, $name) = @_; my($self) = Person1->new($name); # 親のコンストラクタを呼び出す bless($self, $class); $self->{'AGE'} = "(unknown)"; return $self; } # デストラクタ sub DESTROY { my($self) = @_; $self->SUPER::DESTROY; # 親のデストラクタを呼び出す } # メンバ関数(setAge)- 追加 sub setAge { my($self, $age) = @_; $self->{AGE} = $age; } # メンバ関数(getAge)- 追加 sub getAge { my($self) = @_; return $self->{AGE}; } 1;
クラスの継承は、親のクラス名を特殊配列 @ISA に設定することにより実現します。親のクラスを スーパークラス、子供のクラスを サブクラス と呼びます。スーパークラスは特殊クラス名 SUPER で参照することができます。
例では、クラス Person1 のサブクラス Person2 を定義しています。クラス Person1 はメソッドとして getCount()、setName()、getName()、アトリビュートとして NAME、クラス変数として $count を持っていましたが、クラス Person2 はこれらのメソッド、アトリビュート、クラス変数をすべて継承して、さらに、setAge()、getAge() メソッド、AGE アトリビュートが追加されています。
use Person2; $p1 = Person2->new(); $p1->setName("田中"); $p1->setAge(26); print $p1->getName() . "\n"; print $p1->getAge() . "\n";
親クラスのメソッドを書きかえることを オーバーライド と呼びます。オーバーライドは、完全に書きかえることも、元の機能をベースにちょっとだけ機能を付け足すことも可能です。下記のコードを Person2.pm に追加してみてください。親クラス Person1 の getName() をベースにして、前後に [ ... ] を加えるといった、ちょっと付け足しの機能を実現しています。
# スーパークラスのメソッドをオーバーライドする sub getName { my($self) = @_; my($name); $name = $self->SUPER::getName(); return "[$name]"; }
複数の親からメソッドや属性を継承することを 多重継承 と呼びます。例では、@ISA にクラス名のリストを列挙することにより、ClassA と ClassB 両方のメソッドを兼ね備えた、ClassX を定義しています。printAAA() が呼び出された時点で、配列 @ISA に含まれるクラスの中から適切なクラス(先に見付かった方)が選択され、その printAAA() メソッドが呼び出されます。
package ClassA; sub new { my($class) = @_; my($self) = {}; bless($self, $class); return $self; } sub printAAA { print "AAA\n"; } 1;
package ClassB; sub new { my($class) = @_; my($self) = {}; bless($self, $class); return $self; } sub printBBB { print "BBB\n"; } 1;
package ClassX; use ClassA; use ClassB; @ISA = qw(ClassA ClassB); 1;
use ClassX; $p1 = ClassX->new(); $p1->printAAA(); # ClassA の機能も $p1->printBBB(); # ClassB の機能も両方呼び出すことができる
ただし、コンストラクタやデストラクタは ClassA と ClassB のどちらか一方しか呼び出されないので注意してください。