削除
この記事は削除されました。
Perlオブジェクト指向プログラミング入門
Perlオブジェクト指向プログラミングの入門記事です。
クラスの定義
Perlではクラスの定義はpackageを使って行います。
package Point;
コンストラクタ
コンストラクタであるnewというクラスメソッドを定義します。bless関数を使って、オブジェクトを生成します。
package Point; sub new { my $class = shift; # オブジェクトの生成 my $self = bless {@_}, $class; # 初期化処理 return $self; }
コンストラクタを定義すれば、オブジェクトを作成することができます。
my $point = Point->new;
newメソッドは、ハッシュを受け取ることができます。
my $point = Point->new(x => 1, y => 2);
この値は、フィールドに設定されます。
初期化処理
コンストラクタでの初期化処理は以下のように書きます。
sub new { my $class = shift; # オブジェクトの生成 my $self = bless {@_}, $class; # 初期化処理 $self->{x} //= 0; $self->{y} //= 0; return $self; }
//演算子を使って初期化しています。
アクセッサ
次にアクセッサを定義してみましょう。アクセッサとはフィールドにアクセスするためのメソッドのことです。アクセッサを定義すれば、フィールドにアクセスすることができます。
# xを取得・設定するアクセッサ sub x { my $self = shift; if (@_) { $self->{x} = $_[0]; return $self; } return $self->{x}; }
アクセッサを定義するとフィールドにアクセスすることができます。
# 値の設定 $point->x(1); # 値の取得 my $x = $point->x;
継承
継承を行うにはbaseモジュールを使います。Pointを継承したPoint3Dを作ってみましょう。
package Point3D; use base 'Point';
スーパークラスのメソッドの呼び出し
SUPER疑似クラスを使って、スーパークラスのメソッドを呼び出すことができます。
package Point3D; use base 'Point'; # ... sub clear { my $self = shift; # スーパークラスのclearメソッドの呼び出し $self->SUPER::clear; }
コンストラクタのオーバーライド
サブクラスでコンストラクタをオーバーライドしたい場合があると思います。コンストラクタをオーバーライドするには次のようにします。
package Point3D; use base 'Point'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); # 初期化処理 $self->{z} //= 0; return $self; }
newはコンストラクタなので必ずオブジェクト自身を返すようにします。
クラスの作成 - 実践編
Perlのオブジェクト指向プログラミングを実践してみましょう。PointクラスとPoint3Dクラスを作成してみます。
Pointクラス
- Pointは点を表すクラスです。
- xとyというフィールドがあり、アクセッサを持ちます。
- xとyの値を0にクリアするclearというメソッドを持ちます。
package Point; sub new { my $class = shift; # オブジェクトの生成 my $self = bless {@_}, $class; # 初期化処理 $self->{x} //= 0; $self->{y} //= 0; return $self; } # xを取得・設定するアクセッサ sub x { my $self = shift; if (@_) { $self->{x} = $_[0]; return $self; } return $self->{x}; } # yを取得・設定するアクセッサ sub y { my $self = shift; if (@_) { $self->{y} = $_[0]; return $self; } return $self->{y}; } sub clear { my $self = shift; $self->x(0); $self->y(0); } 1;
末尾に「1;」という記述があるのは、モジュールを書くファイルの末尾が真でなければならないというルールがあるためです。
Pointクラスは以下のように使用することができます。
use Point; my $point = Point->new(x => 3, y => 5); print $point->x; $point->y(9); $point->clear;
Point3Dクラス
- Point3Dは3次元の点を表すクラスです。
- x,y,zというフィールドがあり、アクセッサを持ちます。
- x,y,zの値を0にクリアするclearというメソッドを持ちます。
Point3DはPointを継承して作成します。clearメソッドはxとyとzの値をクリアするためにオーバーライドされています。
package Point3D; use base 'Point'; # zを取得・設定するアクセッサ sub z { my $self = shift; if (@_) { $self->{z} = $_[0]; return $self; } return $self->{z}; } sub clear { my $self = shift; $self->SUPER::clear; $self->z(0); } 1;
Point3Dクラスは以下のように利用することができます。
use Point3D; my $point = Point->new(x => 3, y => 5, z => 8); print $point->z; $point->z(9); $point->clear;
実行できるサンプル
実行できるサンプルを書いておきますね。
lib/Point.pm
libというディレクトを作成して、その中にPoint.pmというファイルを作成してください。
package Point; use strict; use warnings; sub new { my $class = shift; # オブジェクトの生成 my $self = bless {@_}, $class; # 初期化処理 $self->{x} //= 0; $self->{y} //= 0; return $self; } # xを取得・設定するアクセッサ sub x { my $self = shift; if (@_) { $self->{x} = $_[0]; return $self; } return $self->{x}; } # yを取得・設定するアクセッサ sub y { my $self = shift; if (@_) { $self->{y} = $_[0]; return $self; } return $self->{y}; } sub clear { my $self = shift; $self->x(0); $self->y(0); } 1;
lib/Point3D.pm
libというディレクトの中にPoint3D.pmというファイルを作成してください。
package Point3D; use base 'Point'; # zを取得・設定するアクセッサ sub z { my $self = shift; if (@_) { $self->{z} = $_[0]; return $self; } return $self->{z}; } sub clear { my $self = shift; $self->SUPER::clear; $self->z(0); } 1;
object.pl
クラスを読み込んで利用するサンプルです。libモジュールを使うことで、モジュールの検索パスを追加できます。
use strict; use warnings; use lib 'lib'; use Point3D; my $point = Point3D->new(x => 3, y => 5, z => 8); print $point->z . "\n"; $point->z(9); print $point->z . "\n"; $point->clear; print $point->z . "\n";
スクリプトの実行
ディレクトリ構成は以下のようにします。
object.pl lib - Point.pm - Point3D.pm
以下のように実行してください。
perl object.pl
出力結果です。
8 9 0
オブジェクト指向プログラミングの詳細
オブジェクト指向プログラミングの詳細については、以下の記事を見てください。
UNIVERSAL - すべてのオブジェクトのスーパークラス
UNIVERSAL
は、すべてのオブジェクトのスーパークラスです。UNIVERSALで定義されているメソッドは、すべてのオブジェクトから呼び出すことができます。
isa
isa
メソッドは、あるオブジェクトが、引数で指定されたクラス、または、指定されたクラスを継承しているをチェックします。
# Pointまたは、Pointを継承 my $isa = $point->isa("Point"); # Point3Dまたは、Point3Dを継承 my $isa = $point->isa("Point3D");
オーバーライド
オーバーライドとは、スーパークラスのメソッドを再定義することをいいます。
package Point; sub move { my $self = shift; my ($x, $y) = @_; $self->{x} += $x; $self->{y} += $y; } package Point3D; use base 'Point'; # moveメソッドをオーバーライド sub move { my $self = shift; my ($x, $y, $z) = @_; $self->SUPER::move($x, $y); $self->{z} += $z; }
オーバーライドの詳しい解説については、継承の記事を見てください。
- Perl ›
- オブジェクト指向プログラミング ›
- here
クラスメソッド
Perlでは、クラスメソッドとは、クラス名を使ってメソッド呼び出しが行われるメソッドのことをいいます。クラスメソッドの定義では、第一引数にクラス名を受け取るようにします。
# クラスメソッド呼び出し MyClass->foo(1, 2); # クラスメソッドの定義 sub foo { my ($class, @args) = @_; }
インスタンスメソッドも参考にしてください。
- Perl ›
- オブジェクト指向プログラミング ›
- here
インスタンスメソッド
Perlでは、インスタンスメソッドとは、インスタンスからメソッド呼び出しが行われるメソッドのことをいいます。インスタンスメソッドの定義では、第一引数にインスタンスを受け取るようにします。単にメソッドとも呼ばれます。
# インスタンスメソッド呼び出し $object->foo(1, 2); # インスタンスメソッドの定義 sub foo { my ($self, @args) = @_; }
クラスメソッドも参考にしてください。
- Perl ›
- オブジェクト指向プログラミング ›
- here
mro - next::methodの有効化・メソッド解決順序の変更
mro
モジュールは、多重継承における、スーパークラスのメソッド解決順序を変更するためのモジュールです。mro
を読み込むと、next::method
が有効になります。多重継承で、スーパークラスのメソッドを探索する場合はSUPER疑似クラスよりも、next::method
うことが推奨されています。
# next::methodの有効化 use mro;
next::method
は、オブジェクトから呼び出します。
# next::methodの使い方 $self->next::method(@args);
next::methodの動作
next::method
は、C3というアルゴリズムで、スーパークラスを探索します。
C3 は常に局所的な優先順位を保存して動作します。 これは、基本的にどのクラスもそのサブクラスより先に現れることはないことを 意味します。 例えば、以下のような古典的なダイヤ型継承パターンを考えます: <A> / \ <B> <C> \ / <D> 標準の Perl 5 MRO は (D, B, A, C) です。 この結果、C は A のサブクラスにも関わらず、A が C より先に 検索されます。 しかし、C3 MRO アルゴリズムでは、(D, B, C, A) の順序になり、この問題は ありません。
参考:C3 の動作 - mro
next::methodを多重継承で使ったサンプル
next::method
を多重継承で使ったサンプルです。
use strict; use warnings; use mro; package MultiBase1 { sub b1_init { my $self = shift; push @{$self->{ps}}, 2; $self->{b1} = 3; } } package MultiBase2 { sub b1_init { my $self = shift; push @{$self->{ps}}, 7; $self->{b1} = 8; } sub b2_init { my $self = shift; push @{$self->{ps}}, 3; $self->{b2} = 4; } } package MultiClass { use base 'MultiBase1', 'MultiBase2'; sub new { my $class = shift; my $self = bless {@_}, ref $class || $class; $self->{ps} //= []; $self->init; return $self; } sub init { my $self = shift; push @{$self->{ps}}, 1; $self->b1_init; $self->b2_init; } sub b1_init { my $self = shift; $self->next::method; } sub b2_init { my $self = shift; $self->next::method; } } my $object = MultiClass->new; print $object->{b1} . "\n"; # 3 print $object->{b2} . "\n"; # 4 print "@{$object->{ps}}" . "\n"; # [1, 2, 3]