vieweditattachhistorytopchangessearchhelp

第66回


■SqueakではじめるSmalltalk入門   第66回


本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今は、簡単なGUIビルダを構築することを目指して、そのために必要なテクニックや知識を整理しているところです。

前回は最後に、a PluggableTextMorphで黄ボタンメニューを機能させることが可能な、次のような短めのコードを示しました。

| field |
field := PluggableTextMorph
  on: Workspace new
  text: nil
  accept: nil
  readSelection: nil
  menu: #codePaneMenu:shifted:.
field bounds: Rectangle fromUser.
field openInWorld


今回は、このように記述するとなぜ、黄ボタンクリックなどでメニューを呼び出すことができるようになるのか、そのしくみを調べてみましょう。

そもそも、クラス「PluggableTextMorph」に「on: Workspace new text: nilaccept: nil readSelection: nil menu: #codePaneMenu:shifted:」というメッセージを送ることで起動されるメソッド「#on:text:accept:readSelection:menu:」は、いったい何をしているのでしょうか。この手の疑問が生じたとき、すぐにソースを確認してみることができるのがSmalltalkシステムのよいところです。

このメールの「on:text:accept:readSelection:menu:」をコピー&ペーストで持ってゆくか、あるいはワークスペースなどで「on:t」までタイプしてからcmd + Qを二度タイプすることで補完した後に、browse it(cmd + B)すると、当該メソッドのソースをブラウズできます。ブラウザには異なるクラスに定義された四つの#on:text:accept:readSelection:menu:が上のリストペインに列挙されますが、今、我々が興味があるのは上から二番目のクラスメソッド「PluggableTextMorph class >> #on:text:accept:readSelection:menu:」です。

PluggableTextMorph class >> on: anObject text: getTextSel
accept: setTextSel readSelection: getSelectionSel menu: getMenuSel

  ^ self new on: anObject
     text: getTextSel
     accept: setTextSel
     readSelection: getSelectionSel
     menu: getMenuSel


self、つまりPluggableTextMorphにnewを送ってインスタンスを作り、それに対してあらためて「on: anObject text: getTextSel accept: setTextSelreadSelection: getSelectionSel menu: getMenuSel」というメッセージを送っていますね。これはnewの送信を省略するためによくあるパターンです。では、そうして起動される同名のインスタンスメソッド「PluggableTextMorph#on:text:accept:readSelection:menu:」のほう(リストの一番目)の定義はどうなっているかというと、

PluggableTextMorph >>on: anObject text: getTextSel
accept: setTextSel readSelection: getSelectionSel menu: getMenuSel

  self model: anObject.
  getTextSelector := getTextSel.
  setTextSelector := setTextSel.
  getSelectionSelector := getSelectionSel.
  getMenuSelector := getMenuSel.
  self borderWidth: 1.
  self setText: self getText.
  self setSelection: self getSelection


というような記述であることが分かります。ほとんどはインスタンス変数への対応するパラーメータの代入(束縛)です。冒頭のスクリプトで最後のパラメータ(menu:キーワードのところ)であるところの、シンボル「#codePaneMenu:shifted:」は、getMenuSelを介して、getMenuSelectorというインスタンス変数に束縛されることも分かります。

では、このgetMenuSelectorというインスタンス変数は、PluggableTextMorphとそのスーパークラスに定義された他のどのメソッドでどのように使われているのでしょうか。こうした情報についても、ブラウザ中央の「inst vars」ボタンをクリックしてポップアップするメニューから「getMenuSelector」を選択することで簡単に調べることができます。

[fig.A]インスタンス変数に関与しているメソッドを探すためのボタン
Uploaded Image: 66a.png

結果、#on:text:accept:readSelection:menu:以外では、#hasYellowButtonMenuと#getMenu:という二つのメソッドで参照されていることが分かります。それぞれの内容をざっと確認すると、どうやら後者が黄ボタンメニューのポップアップを司るメソッドであるようです。

[fig.B]getMenuSelectorにアクセスしているメソッド一覧とそのソース
Uploaded Image: 66b.png

手続きの流れをざっと見ると、getMenuSelectorにメッセージ「numArgs」を送信してパラメータの数を得、その結果によって条件分岐をしていますね。冒頭のスクリプトの流れでは、getMenuSelectorには#codePaneMenu:shifted:が束縛されているので、実際にnumArgsを調べてみましょう。

#codePaneMenu:shifted: numArgs   " => 2 "


この式をprint it(cmd + P)するとパラメータが二つであることを意味する「2」を返してくるはずです。もっとも、Objective-C同様、Smalltalkのセレクタは、原則として、コロンの数が引数の数と一致するので、あらためてnumArgsを送ってみるまでもないのですが…。

さて。では改めて「getMenuSelector numArgs = 2」のところで何をしているか見てみると、

model perform: getMenuSelector with: menu with: shiftKeyState


という式が見つかります。#perform:with:with:は、第一パラメータをセレクタに含むメッセージをレシーバに送るメソッドなので、結果的に、次の式を評価するのと同じことになります。

model codePaneMenu: menu shifted: shiftKeyState


この際の作業について詳しくは、StringHolder >> #codePaneMenu:shifted:の定義を見ていただくとして、端的には、モデルに対して、黄ボタンメニューを作って返してくるよう“丸投げ”しているわけです。専門用語では「委譲」というテクニックですね。

a PluggableTextMorphを作る際、モデルと、そこにあらかじめ定義した黄ボタンメニューを返すメソッド名(セレクタ)を第一および第五パラメータとして添える必要があるのはこうした理由からです。どんなメニューをポップアップするかは、a PluggableTextMorph自身は関知せず、すべてモデル次第…というわけです。

同時にこのことは、クラス「PluggableTextMorph」の定義を変えずに、そのインスタンスの挙動を変化させるしくみが働いていることを意味します。PluggableTextMorphが、その名に“Pluggable”を冠している理由が、ちょっと分かったような気がしてきませんか?

このページを編集 (5449 bytes)


Congratulations! 以下の 1 ページから参照されています。

This page has been visited 739 times.