第24回
■
■SqueakではじめるSmalltalk入門 第24回
■
本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回は、「再定義していないメソッドが、なぜ多態性を発揮できるのか」についてです。
簡単のため、2つある出納用メソッドのうち、#deposit:のみに注目します。次のようなスクリプトをdo it (cmd+D)した(あるいは、stockの中身を観察するためにinspect it (cmd+I)した)と想定します。
| stock |
stock _ StockAccount new.
stock deposit: 300.
^ stock
ここで、stockに束縛されたa StockAccountは、「deposit: 300」というメッセージを受け、自らが属するクラスStockAccountで、#deposit:というメソッドを探します。しかし、我々はそれを再定義していないので、該当するメソッドを見つけることは叶いません。しかたがないのでスーパークラスであるBankAccountにおいて改めて#deposit:を探し、見つけてそれを起動します。【註】
BankAccount >> deposit: aNumber
self balance: self balance + aNumber
すでにご存じのとおり、もともとこのメソッドは、selfにa BankAccountが束縛されることを念頭に記述されたものです。その振る舞いは、self balanceの返値とaNumberの和をパラメータにして、「balance: …」というメッセージをselfに送る…というものです。selfがa BankAccountなら、「balance」、「balance: …」というメッセージはそれぞれ対応するBankAccount >>#balance、BankAccount >> #balance:を起動して話はおしまいです。
BankAccount >> balance
^ balance ifNil: [balance _ 0]
BankAccount >> balance: aNumber
balance _ aNumber
しかし、今selfには、このメソッドを起動するきっかけを作ったメッセージ式におけるレシーバ、つまり、a StockAccountが束縛されています。したがって、このコンテキスト(文脈、あるいは、メソッド実行時のインタープリタの内部状態)において、「balance」および「balance: …」というメッセージを受けたself、つまりa StockAccountは、自らの属するクラスStockAccountにて#balance、#balance:メソッドを探し、見つけることができれば(当然、我々はそうしたメソッドをStockAccountに再定義済みなので必ず見つかるわけですが)それらを起動します。
StockAccount >> balance
^ self pricePerShare * self numShares
StockAccount >> balance: aNumber
self numShares: aNumber asFloat / self pricePerShare
この2つのメソッドは、インスタンス変数balanceへのアクセスを行なうBankAccount >> #balance、#balance:と、同名で、同種のメッセージで起動される点では一緒なのですが、その内容、つまり振る舞いはまるで異なります。したがって結果的に、これらのメソッドを起動するBankAccount >> #deposit:や#withdraw:も、その振る舞いを変える、つまりレシーバに合わせて相応しい多態性を発揮できる、というわけです。少々込み入った話になってしまいましたが、以上が、再定義なしに出納用メソッドがうまく機能する“からくり”です。
もちろん、スーパークラスのメソッドに、このような振る舞いをさせるには、設計段階からの準備が必要です。たとえば、当初、出納用メソッドを定義する際に候補にあった、インスタンス変数balanceへの直接参照で記述してしまっていたならば、単純にそれらをサブクラスで再定義するしかStockAccountを実現する方法はなくなります。
BankAccount >> deposit: aNumber
balance _ balance + aNumber
BankAccount >> withdraw: aNumber
balance _ balance - aNumber max: 0
↓
StockAccount >> deposit: aNumber
numShares _ numShares + (aNumber asFloat / pricePerShare)
StockAccount >> withdraw: aNumber
numShares _ numShares - (aNumber asFloat / pricePerShare) max: 0
すでに扱った初期化の問題を無視すれば、確かにこれでうまくStockAccountは機能します。もっともこの場合、結果として、スーパークラスBankAccountのメソッドをすべてオーバーライドした状態になるので、ここで継承を使う“うま味”はまったくなくなってしまうのですが…。
註:メソッドのメッセージパターンには、本来不要な「クラス名 >> 」という表現を追記して、そのメソッドがどのクラスに帰属するのかを明示的にすることがあります。もしブラウザなどにコピー&ペーストしてそのまま使用するときは、「クラス名 >> 」の部分は選択せずに、それを省いた残りをコピーするようにしてください。
このページを編集 (3948 bytes)
|
以下の 1 ページから参照されています。 |
This page has been visited 881 times.