オブジェクト指向プログラミング ( Object Oriented Programing : OOP ) とはプログラムを作成する際の手法の事で、Perl はバージョン 5からこの機能をサポートしていますので使用することが出来ます。 何が変わるの?と言うと、プログラムをオブジェクト(クラス)という独立した単位で作成し、データとふるまいを定義します。
例えば車を例に取って考えるとその車の属性とふるまいは次のような感じになります。
[ 車クラス ]
| 属性 |
色、燃料、走行距離 |
| ふるまい |
走る、停止する、給油 |
|
このようにクラスでデータとそれに対するふるまいを定義してデータのアクセスを制限することによってクラスは
カプセル化されます。 そうすればクラスを利用する場合は内部構造まで知らなくてもそのインターフェースだけを知ればそのオブジェクトを操作することが出来ます。 この事を
ブラックボックス化と呼びます。 また、データとふるまいを併せて独立させ作成することによりそれを部品として扱え再利用することが出来、生産効率の向上になります。
Perlでは通常クラスをパッケージ ( 大抵はモジュール )として記述します。 前述した属性とはそのオブジェクトに属する変数で、ふるまい ( メソッド )はそのオブジェクトに含まれる関数を指すことになります。
次で詳しい説明をしていますので、先ずは下のソースを見てみましょう。
# Car のパッケージ名を設定
package Car;
# コンストラクタ
sub new{
my $this = shift;
my ( $color, $gus, $mile ) = @_ ;
my $car = {"color" => $color ,
"gus" => $gus ,
"mile" => $mile } ;
bless $car, $this;
return $car ;
}
# main のパッケージ名を設定
package main;
my $cls = new Car "Black", 100, 0 ;
先ず下のコードですが、通常は異なるパッケージに属するサブルーチンは通常 [
&car::new('Black', 100, 0) ] というような記述をします。 モジュールを使ったオブジェクト指向的な記述は少し変わって、このような記述になります。 これによってパッケージにあるサブルーチン [
new ] が呼び出され、戻り値としてオブジェクト [ ハッシュ ] のリファレンスが変数に代入されます。
[ 1 ]
my $cls = new Car "Black", 100, 0 ;
ここではクラスの引数を受け取るはずですが 1.で指定した引数より一つ数が多い事に気付かれた方もいらっしゃると思います。 1.のような記述で呼び出すと第一引数としてそのクラス名 ( パッケージ ) が渡されます。このサブルーチンで
print $this を実行すると [
Car ] という表示がされます。 このパッケージ名の事ですがこれは次の 3.で使用します。
[ 2 ]
my $this = shift;
bless関数は、リファレンスとパッケージ名を引数として呼び出します。この関数を実行する事で第1引数で与えられたリファレンスが指すオブジェクトは、第2引数で指定したクラスに所属すると関連付けをする事が出来ます。
[ 3 ]
bless $car, $this;
[ 4 ]
サブルーチン [ new ] は 1.で呼ばれ、オブジェクトを生成初期化する特殊なメソッドです。このようなメソッドをコンストラクタと呼び、コンストラクタはオブジェクトが生成されるときに一度だけ実行されます。
sub new{〜}
変数へのアクセス
上の例は単にオブジェクトを生成しているだけでした。[ bless関数 ] によって関連付けられた変数へのアクセスは次のようにします。
$cls->{gus} # ハッシュ{gus}へのアクセス
※ただしこのようなアクセスはクラス内の変数へ直接アクセスしているので、オブジェクト指向的にはあまり良いやり方とは言えません。 通常はアクセスメソッドと呼ばれる関数を作成しそれを介して変数にアクセスする方法を取るのが一般的です。
メソッドの追加
今度は上の例にふるまい ( メソッド ) を追加して、それを呼び出してみます。
# Car のパッケージ名を設定
package Car;
# コンストラクタ
sub new{
my $this = shift;
my ( $color, $gus, $mile ) = @_ ;
my $car = {"color" => $color ,
"gus" => $gus ,
"mile" => $mile } ;
bless $car, $this;
return $car ;
}
# メソッドの定義
sub drive{
$this = shift ;
$this->{gus} = $this->{gus} - 10 ;
$this->{mile} = $this->{mile} + 10 ;
print "10キロ走りましたので合計$this->{mile}走りました\n" ;
print "燃料は$this->{gus}になります。\n" ;
}
# main のパッケージ名を設定
package main;
my $cls = new Car "Black", 100, 0 ;
$cls->drive() ;
メソッドを呼び出す場合は [ new ] の戻り値で取得したオブジェクトの実態 [ インスタンスと呼びます ] に対して行います。
[ 1 ]
$cls->drive() ;
サブルーチンは以下の通りです。 ここでも引数で定義していませんが第一引数にハッシュのリファレンスが渡されます。 通常は各変数は [ new ] で myのレキシカルとして宣言されていますのでアクセスは出来ないはずですが、このサブルーチンに渡された第一引数を用いる事によって次のような記述で参照可能になります。
[ 2 ]
sub drive{
$this = shift ;
$this->{gus} = $this->{gus} - 10 ;
$this->{mile} = $this->{mile} + 10 ;
print "10キロ走りましたので合計$this->{mile}走りました\n" ;
print "燃料は$this->{gus}になります。\n" ;
}
アクセスメソッドの追加
変数へのアクセスの時に 「 このやり方はあまり良くありません 」 と記述しましたが、通常は変数にアクセスする事を目的としたメソッドを用意します。それを介して変数の値を操作、取得するというやり方を用います。このようなメソッドの事を
アクセスメソッドと呼びます。
# Car のパッケージ名を設定
package Car;
# コンストラクタ
sub new{
my $this = shift;
my ( $color, $gus, $mile ) = @_ ;
my $car = {"color" => $color ,
"gus" => $gus ,
"mile" => $mile } ;
bless $car, $this;
return $car ;
}
# メソッドの定義
sub drive{
$this = shift ;
$this->{gus} = $this->{gus} - 10 ;
$this->{mile} = $this->{mile} + 10 ;
print "10キロ走りましたので合計$this->{mile}走りました\n" ;
print "燃料は$this->{gus}になります。\n" ;
}
# アクセスメソッドの定義
sub gus{
$this = shift ;
if( $_[0] ){
$this->{gus} = $_[0];
}
return $this->{gus};
}
# main のパッケージ名を設定
package main;
my $cls = new Car "Black", 100, 0 ;
$cls->gus(10) ;
print $cls->gus();
通常のメソッドと記述は同じですがこのメソッドに引数を与えて呼び出すとクラスの変数を変更する事が出来ます、逆に右辺で呼び出す事によって変数を取得する事が出来ます。
デストラクタ
Perlでは生成したオブジェクトを明示的に開放しなくてもPerlが自動的にスコープが外れた時か、参照が無くなった時にオブジェクトを自動的に開放してくれます。又blessされたオブジェクトはその開放の直前に時に特殊なメソッドが呼ばれる事になります。この特殊なメソッドをDESTROYと呼びます、ただしこのメソッドはクラス内にDESTROYが記述されていれば呼び出されますが、記述が無い場合は何も起こりません。
# Car のパッケージ名を設定
package Car;
# コンストラクタ
sub new{
my $this = shift;
my ( $color, $gus, $mile ) = @_ ;
my $car = {"color" => $color ,
"gus" => $gus ,
"mile" => $mile } ;
bless $car, $this;
return $car ;
}
# デストラクタ
sub DESTROY{
my $this = shift;
print "廃車\n";
}
# メソッドの定義
sub drive{
$this = shift ;
$this->{gus} = $this->{gus} - 10 ;
$this->{mile} = $this->{mile} + 10 ;
print "10キロ走りましたので合計$this->{mile}走りました\n" ;
print "燃料は$this->{gus}になります。\n" ;
}
# アクセスメソッドの定義
sub gus{
$this = shift ;
if( $_[0] ){
$this->{gus} = $_[0];
}
return $this->{gus};
}
# main のパッケージ名を設定
package main;
my $cls = new Car "Black", 100, 0 ;
$cls->gus(10) ;
print $cls->gus();
これで $cls がスコープから外れた時 ( この例だとスクリプトが終了時 ) にサブルーチン [ DESTROY ] に制御が移り最後に廃車という文字列が出力されます。
モジュールの作成
これまでは一つのファイルに全て記述をしていましたが、コードの記述が数百行にもなったり、作業を分担する場合には不便だと思いますので、先程まで作成していたコードを2つのファイルに分割してみましょう。
[ Car.pm ]
package Car;
# コンストラクタ
sub new{
my $this = shift;
my ( $color, $gus, $mile ) = @_ ;
my $car = {"color" => $color ,
"gus" => $gus ,
"mile" => $mile } ;
bless $car, $this;
return $car ;
}
# デストラクタ
sub DESTROY{
my $this = shift;
print "廃車\n";
}
# メソッドの定義
sub drive{
$this = shift ;
$this->{gus} = $this->{gus} - 10 ;
$this->{mile} = $this->{mile} + 10 ;
print "10キロ走りましたので合計$this->{mile}走りました\n" ;
print "燃料は$this->{gus}になります。\n" ;
}
# アクセスメソッドの定義
sub gus{
$this = shift ;
if( $_[0] ){
$this->{gus} = $_[0];
}
return $this->{gus};
}
1;
[ sample.pl ]
use Car;
my $cls = new Car "Black", 100, 0 ;
$cls->drive() ;
Car.pm の末尾に [ 1; ] が追加されています。 これはモジュールが正常に読み込まれた事を表わす為に真となる値をモジュールの最後に評価させています。 この記述がないとエラーになってしまいます。
引数で指定したモジュール [ .pm ] から、現在のパッケージにへ内容をインポートします。この関数は特殊変数
@INC に含まれているフォルダから順に対象のモジュールを検索します。見つからなければコンパイル時にエラーとなります。