俺による俺のためのオブジェクト指向講座。その5
真夏のピッチングでヘバりながらも第五回。
「関数呼び出し」と「メソッド呼び出し」
Perlは厳格なOO言語ではないので、定義したサブルーチンは関数でありメソッドであるという状態になる。
関数とメソッドの違いは、「クラスに属しているか」の1点に集約される。
つまり、関数は「言語の一機能」として呼び出されるのに対して、メソッドは「クラスの一機能」として呼び出される。
この違いは呼び出されるサブルーチン内で現れる。
例えば、MyAppクラスのfooサブルーチンをそれぞれの呼び方をすると
package MyApp; sub foo { my @args = @_; print join(",", @args); } package main; # 関数呼び出し MyApp::foo('bar', 'buzz'); # bar,buzz # メソッド呼び出し MyApp->foo('bar', 'buzz'); # MyApp,bar,buzz
サブルーチン側で受け取った引数に変化がある。
ダブルコロン(::)は「完全修飾」で、単に「MyAppのfoo」と指定している。
アロー演算子(->)は一種のデリファレンスで、「MyAppによるfoo」という指定を意味する。
アロー演算子を用いてサブルーチンを呼び出すとメソッド呼び出しと解釈され、第一引数にアロー演算子の左側が渡される。
これによって、呼び出されるサブルーチンが「自分がどのパッケージのメソッドとして呼ばれたか」を知ることができる。
OOPでは「どのようなクラスに関する操作か」「どのようなクラスに作用する操作か」を知っていないと操作する対象を特定することができないので、関数呼び出しは「すべきではないもの」と言われている。
コンストラクタ
あるクラスのインスタンスが生成されたときに呼ばれるメソッドをコンストラクタと言う。
全てのクラスは(1つ以上の)コンストラクタを持たなくてはならない。
※コンストラクタは、メソッド名や戻り値の型などに制限がある場合もある(言語による)。
コンストラクタの役割は「初期化」で、主にプロパティのデフォルト値をセットしたり、引数に与えられた値をセットしたり、そのクラスが必要とするリソースのロードを行う。
各インスタンスに対して1回だけ実行しておけば良い内容はコンストラクタ中で行う、と覚えておくと良い。
コンストラクタについては、後に継承を説明するときに追記する。
デストラクタ
コンストラクタの対称に位置するのがデストラクタというメソッド。
つまり、あるインスタンスが破棄されるときに呼ばれるメソッドのことだ。
コンストラクタとは違い、デストラクタは空(未定義)でも構わない。
普通のプロパティ等はどちらにせよガベージコレクション(以下GC)で解放されるので、デストラクタで明示的に操作する必要はない。
デストラクタを作成すべき場面は、例えばDBへのコネクションやファイルをオープンしているクラスがある。
コンストラクタで生成したリソースはデストラクタで破棄するのがマナー(リソース確保・解放についての考え方は割愛する)であることと、インスタンスがどのように破棄されたとしても原則的にデストラクタは呼び出されることがその理由。
言語によっては開いたリソースも勝手にクローズしてくれる場合があるので、デストラクタはあまり重視しなくても良い。
Perlでのコンストラクタとデストラクタ
前述の通りPerlはOOPに対して厳格ではない。
それどころかOOPのための組み込み機能を多くは提供していないので、OOPの概念をエミュレートして実装するという感覚になる。
もちろん先達によって「事実上の標準」が作られてはいるので、まずはそこから始める。
他の言語との違和感を減らすため、コンストラクタはnewという名前で作成されることが多い。
package MyApp; sub new { my $pkg = shift; my %props = @_; return bless \%props, $pkg; } package main; my $app = MyApp->new(%some_props);
これが簡単なコンストラクタとその使い方。
コンストラクタもメソッドなのでメソッド呼び出しされることを前提に作る。
第一引数にクラス名が与えられるので、その名前を引数に与えられた仮のプロパティにblessする。
そうしてできたインスタンスを返すことで、呼び出し元が目的のオブジェクトを手に入れることができる。
次にデストラクタ。
Perlはコンストラクタに制限が無いがデストラクタには1つだけ決まりがある。
それは名前をDESTROYとして定義することだ。
デストラクタはPerlのGC中に自動で呼ばれるのでPerl自身が名前を知っていなければならず、またPerlが呼ぶ関数は全て大文字で定義するのが慣例となっているので、このようになっている。
それ以外には特に何もないので、内容は自由に記述して良い。
package MyApp; sub new { my $pkg = shift; my %props = @_; return bless \%props, $pkg; } sub DESTROY { my $self = shift; print 'destroyed ...'; } package main; my $app = MyApp->new(%some_props); $app = undef; # MyApp::DESTROY is called. print 'destroyed ...'
インスタンスが破棄されると、デストラクタの内容が処理される。
$appにundefを代入したタイミングで$appの中身が消滅したと判断されオブジェクトが破棄されるので、デストラクタのprint文によって文が出力される。
というわけで
ここまで。
次回はPerlOOPのセオリー続編。