ObjectSquare [1999 年 4 月号]

[Happy Squeaking!!]


5.Squeak演習: インターセッション

5.1 インターセッション初歩


インターセッションのもっとも簡単な例として、メタ情報を利用してインスタンスの状態や振る舞いを変更させることをしてみましょう。


オブジェクトの状態を変更する

今回は、以前の例にもでてきた、銀行口座クラス(Account) からインスタンスを作り、インターセッションの機能を使い属性の値を変更してみることにします。

以下のリンクからソースのダウンロードをしてください。
BankApplication.st (<= CLICK NOW)

ファイルリストを使いSqueak内にロードします。やり方は第三回の「サンプルコードの読み込み」を参照してください。

まずは銀行口座のインスタンスを生成し、行儀よく値を設定します。

| acc | 
acc := Account new.
acc initialize.
acc deposit: 1000.
acc withdraw: 100.
acc inspect

以下のようなインスペクタが立ち上がります。ちゃんと値が設定されています。


Accountインスタンスのインスペクト

今、Accountにはidを外から設定するためのメソッドが定義されていません。従ってidの値はinitialize後は常に0になっています。

しかしこのような場合でも、相手がどのような名前の変数を属性として持っているかがわかっていれば書き換えが可能です。インターセッションを利用して値を設定してみましょう。

accのクラス、Accountが、どのような変数を属性として定義しているかは、acc class instVarNames とすることでわかります。ここから'id'という変数がAccountクラスの何番目に定義されているかを知り、次にここで得たインデックスを用いて、最後に変数の値を書き換えるメッセージを送ることとします。

| acc  varNames index | 
acc := Account new.
acc initialize.
acc deposit: 1000.
acc withdraw: 100.
varNames := acc class instVarNames. "変数のリストを得る"
index := varNames indexOf: 'id'. "'id'変数が何番目に定義されているか"
acc instVarAt: index put: 2. "値の書き換え"
acc inspect.

acc inspectの前に、以下を追加してもう一度"do it"してください。今度は見事にidの値が書き換わっています。


インターセッションで値を変更したAccountインスタンスのインスペクト

Squeakの場合は、instVarNamed:put: も使えますのでそれを使えば上記のコードはもっと短くてすみます。(余裕のある方は、instVarNamed:put:の実装を見てみてください)。

indexOf:についてははじめて出てきました。集合オブジェクトに投げることが出き、特定のオブジェクトが集合の何番目に含まれているかを調べるメッセージです。
SmallTip: 集合のインタンスに含まれる特定要素のインデックスを知りたい場合には、indexOf:を用いる
instVarAt: put: を使えば、オブジェクトが内部に保持しているはずの変数を自由に書き換え可能なので、カプセル化が壊れることになります。Account側でカプセル化をどうしても守りたい場合は、instVarAt:put自体をオーバーライドして、アクセス不要にしてしまう方法があります。

オブジェクトの操作を間接的に起動する

次は、属性のアクセスではなく、操作の起動を行うことにします。

Accountクラスで定義された操作名の集合を得て、そこから使えそうな操作を選択。最後に実際の操作の起動という流れです。
以下のようになります。

| acc opNames maybeOp |
acc := Account new.
acc initialize.
opNames := acc class selectors. "操作名の集合を得る"
maybeOp := #deposit:. "多分あるだろうという操作"
(opNames includes: maybeOp) "集合に含まれていた場合に"
        ifTrue:[ acc perform: maybeOp with: 1000]. "間接的に実際の起動"
acc inspect.

操作の集合は Set (initialize deposit: withdraw: getBalance )となります。これに、有るだろうという操作のセレクタ(#depposit:)が含まれるかをincludes:で確かめます。

SmallTip: 集合のインスタンスに特定要素が含まれるかを確かめる場合にはincludes:を用いる

含まれていた場合には実際にメッセージを送ります。ここで、直にdeposit:1000と書いていないところが今回のポイントです。このようにすることで、maybeOp変数に代入されるセレクタの名前を変更するだけで、いろいろな場合に対処できる可能性がでてきます。(この例では、ほとんど影響はないですが、より本格的なものを作るときには有効です)。

結果は以下の通り、ちゃんとdeposit:が起動されていることが確認できます。


perform: によりdeposit操作がおこなわれたAccountのインスタンス

Smalltalkでは perform: というメッセージにより、間接的なメッセージの起動を行なうことができます。
基本的な記法は
perform: (送りたいメッセージ名) with: (送りたいメッセージの引数)
となります。

送りたいメッセージの引数に応じて、幾つかのバリエーションが存在します。

perform: (引数なし)
perform:with: (引数一つ)
perform:with:with: (引数二つ)
perform: withArguments: (引数を配列にして設定)

acc initialize と書かずに、acc perform: #initialize と書けるということです。
メッセージ名(セレクタ)は、単なるシンボル(書き換え不能な文字列)であり、変数を使うなどして自由に設定できます。上記の場合は、maybeOpに #deposit:を代入して利用しています。

もう少し例を示しましょう。

String new perform: #size.
Transcript perform: #show: with: 'Hello'.
'HelloSmalltalk' perform: #copyFrom:to: withArguments: #(1 5).
SmallTip: perform:を利用することで、メッセージ名による間接的なメッセージの起動ができる

© 1999-2001 OGIS-RI Co., Ltd.

Prev.

Index

Next