ヘルパー
SenseTalkオブジェクトは単独で存在する必要はありません。他のオブジェクトが一部または全部の動作を補助(helped
)することが可能です。例えば、誕生日に基づいてその人の年齢を計算するハンドラーを持つ人物オブジェクトを作成した場合、その人物オブジェクトは他のオブジェクトのヘルパーとして使用でき、それらも年齢を計算することが可能になります。そのためには、それら自身のハンドラーは不要です。
以下に、RexとSueという名前の他の二つのオブジェクトによってヘルプされるオブジェクトのサンプルスクリプトを示します:
sayHello -- 最初に友好的な態度で始めましょう!
properties
helpers: [Rex, Sue],
birthdate: "May 14, 1942",
end properties
on sayHello
put "Greetings! My age is " & calculateAge()
end sayHello
このオブジェクトのsayHello
ハンドラーはcalculateAge
という名前の関数を呼び出します。sayHello
ハンドラーが呼び出されると、それはcalculateAge()
を呼び出します。このオブジェクトにはcalculateAge関数ハンドラーがありませんが、もしヘルパーの一つ、例えばRexがそのような関数ハンドラーを持っているなら、そのハンドラーはこのオブジェクトの代わりに実行され、年齢を計算します。
Who is Me? What is This Object?
オブジェクトとそのヘルパーは密接に連携し、まるで一つのオブジェクトのように動作します(三銃士のスローガン、「一人のために全員、全員のために一人」を思い浮かべてください)。ヘルパー内のハンドラーが実行されるとき、それは元のオブジェクトのハンドラーであるかのように扱われます。このコンテキストでは、me
とmy
はヘルパーではなく、ヘルプされるオブジェクトを指します。
これは、ヘルパーのスクリプト内で実行される任意のステートメントがそのメッセージをヘルプされるオジェクトにターゲットし(そして間接的にヘルパーに対して)、ヘルパーがme
またはmy
を使用してプロパティにアクセスするとき、ヘルプされるオブジェクトのプロパティにアクセスすることを意味します。スクリプトが自身をターゲットにするか、または自身のプロパティにアクセスする必要がある場合、me
の代わりにthis object
という用語が使われることがあります。
ヘルパーのメッセージパス内の位置
オブジェクトのヘルパーは、メッセージパス内でそのオブジェクトと密接に関連しています。オブジェクトがハンドラーを持っていないメッセージ(または取り扱ってからパスする)を受け取ると、そのメッセージは次にオブジェクトのヘ ルパーに行きます。ヘルパーがメッセージを取り扱わない場合のみ、メッセージはメッセージパス内の他のオブジェクト、たとえばbackScripts
内のオブジェクトにパスされます。
ヘルパーに設計されたオブジェクト
どのオブジェクトも、他の一つまたは複数のオブジェクトのヘルパーとなることができます。例えば、RexというオブジェクトがcalculateAge()
関数ハンドラーを持っていると仮定します。そして、同じcalculateAge()
機能が必要な別のオブジェクト、Lunaがあると仮定します。RexをLunaのヘルパーのリストに追加することで、Lunaはその能力を獲得します。しかし、Rexは他にもハンドラーを持っているかもしれません。それら全てがLunaの行動の一部として含まれることが望ましいわけではありません。Lunaが必要とする機能だけをどのように提供することができるでしょうか?
このような状況に対処する一つの良い方法は、複数の異なるオブジェクトに有用かもしれないその行動(ハンドラー)を分け出し、特にヘルパーとしての役割を果たすために設計された新しいオブジェクトを作成することです。例えば、calculateAge()
ハンドラーをRexからPersonオブジェクトに移動し、それからPersonをRexとLunaのヘルパーリストに追加するかもしれません。各オブジェクトは任意の数のヘルパーを持つことができますので、関連する行動のグループをヘルパーオブジェクトに分け出すことは非常に強力なツール となります。それにより、異なるオブジェクトでその機能をさまざまな組み合わせで再利用することが可能となります。
ヘルパーとなるオブジェクトの設計
多くのヘルパーオブジェクトは非常にシンプルで、数個のハンドラーを持つスクリプトだけを持っています。例えば、Personオブジェクトは、私たちが話してきたcalculateAge()
関数のような単一のハンドラーで始まるかもしれません:
to calculateAge -- returns my current age in years
return (the date - my birthDate) / 365.25 days
end calculateAge
もっと私(ああ私!)について
ヘルパーについて理解するための非常に重要なことの一つは、ヘルパーのスクリプト中でme
とmy
という用語が使われるとき、それらはヘルパーとそのプロパティではなく、助けられるオブジェクトとそのプロパティを指すということです(必要に応じてヘルパーを指すためにthis object
を使用します)。これは通常、オブジェクトの一部として書かれたハンドラーは、別のオブジェクトを助けるときも、何の変更もなく正しく動作することを意味します。
他のオブジェクトに基づくオブジェクトの作成
一つのオブジェクトを他のオブジェクトのヘルパーとして使用し、最初のオブジェクトの行動を借りることは非常に簡単で、特別な努力を必要としないことがよくあります。あなたがスクリプトの一部の機能を、主に他のオブジェクトのヘルパーとして機能するオブジェクトに分けることを決定したら、活用したい追加の機能がいくつかあるかもしれません。
このセクションの前半で、new object
表現を使用して単純なオブジェクトを作成する方法を見ました。new object
表現を使用する一般的な方法は、新しく作成するオブジェクトがどのようなタイプのものであるかを指定するために、そのオブジェクトが基になるものを提供することです。これは次の例で示されています:
put new Person with (name:"Elizabeth", age:14) into daughter
ここで、Personは新しく作成されるオブジェクトを定義するために使用されるオブジェクト("プロトタイプ"オブジェクトと呼ばれる)の名前です。この例では、SenseTalkは"Person"という名前のオブジェクトを探し、そのオブジェクトに初期プロパティをパラメータとしてmakeNewObject
関数メッセージを送ります。これにより、プロトタイプオブジェクトは新しいオブジェクトがどのようなものになるかを完全に制御するチャンスを得ます。通常、新しいオブジェクトはプロトタイプオブ ジェクトそのものに基づいて作られ、プロトタイプオブジェクトをヘルパーとして持つことでその全ての行動を継承し、プロトタイプの通常のプロパティ(スクリプトとヘルパー以外)のコピーを受け取ります。それでは、これがどのように機能するかをもう少し詳しく見ていきましょう。
プロトタイプオブジェクトの役割
もしプロトタイプオブジェクトがmakeNewObject
関数を持っている場合、その関数は新しいオブジェクトを作成して返すべきです。もしmakeNewObject
メッセージを処理しない場合、SenseTalkの組み込みのmakeNewObject
関数が呼び出され、新しいオブジェクトを作成します(この例では、Personがヘルパーとなる)。そのオブジェクトのnew
式で提供されたすべてのプロパティを設定し(このケースでは"name"と"age")、すでに存在しないプロトタイプからの任意のプロパティを追加し、そのオブジェクトに"initialize"メッセージを送ります。したがって、組み込みの振る舞いの効果は、このmakeNewObject
関数と基本的に同じです:
makeNewObject初期プロパテ ィへ
私を助ける初期プロパティを持つ新しいオブジェクトを取得
私のプロパティをそれに追加
"initialize"をそれに送る
それを返す
makeNewObject終了
もし異なる振る舞いが必要な場合は、単にプロトタイプオブジェクトにカスタムmakeNewObject
関数を書きます。例えば、初期プロパティ値をいくつかデフォルトで設定したり、追加のヘルパーを含めたり、あるいは新しいオブジェクトを作るために別のプロトタイプオブジェクトを呼び出すこともあります。
デフォルトの振る舞いは、プロトタイプオブジェクトを助ける新しいオブジェクトを作成することであることに注意してください。そのため、任意のオブジェクトをプロトタイプオブジェクトとして使用することができ、それが新 しいオブジェクトの主要なヘルパーとなります。
新しいオブジェクトのプロパティの初期化
プロトタイプから新しいオブジェクトが作成されると、デフォルトではプロトタイプオブジェクトの全ての振る舞いを継承し、そのプロパティのコピーも受け取ります。また、new
式で提供される任意のプロパティも受け取ります(これはプロトタイプからの対応するプロパティ値を上書きします)。プロトタイプオブジェクトは新しいオブジェクトに対して追加のプロパティを提供することもできます。これを行う一つの方法は、既に述べたように、カスタムmakeNewObject
関数を書くことですが、これはまれにしか必要ありません。よりシンプルで(そしてより良い)選択肢は、組み込みのmakeNewObject
関数によって新しいオブジェクトに送られる"initialize"メッセージを利用することです("initialize"メッセージはオブジェクトとそのヘルパーにのみ送られ、フルメッセージパスを通じては送られません)。以下に、いくつかのデフォルトのプロパティ値を提供するinitializeハンドラの例を示します:
initializeへ
プロパティ{time:"12:00", priority:"Normal"}を私に追加
initialize終了
もしこのハンドラが"May"という値を持つmonthプロパティを持つプロトタイプAppointmentオブジェクト内にあるなら、新しいappointmentは以下のように作成できます:
新しいAppointmentを{time:"8:30"}で作成してmeetingに入れる
これは、時間プロパティが"8:30"に設定され、優先度が"Normal"に設定された新しいオブジェクトを作成します。なぜなら、add properties
コマンドは既に存在するプロパティを置き換えないからです。したがって、new
式で提供された値は、このinitializeハンドラによって提供されたものより優先されます。新しいオブジェクトはまた、プロトタイプオブジェクトからコピーされた"May"という値を持つmonthプロパティも持つでしょう。
空のオブジェクトの作成
時々、プロトタイプから全てのプロパティのコピーを自動的に受け取る新しいオブジェクトを作成したくないことがあります。これを行うには、プロトタイプの前にempty
という言葉を使用します:
新しい空のAppointmentを{time:"8:30"}で作成してmeetingに入れる
ここでは、新しいオブジェクトはAppointmentによって補助され、そのtimeプロパティは"8:30"に、priorityプロパティは"Normal"に設定されます(initializeハンドラから)。プロトタイプオブジェクトから直接コピーされたmonthプロパティ(またはその他のプロパティ)を受け取ることはありません。
オブジェクトをテキストとして表示する
オブジェクトがテキストとして表示されるとき、デフォルトではそのキーと値はplistPrefix
、plistSuffix
、plistKeySeparator
、plistEntrySeparator
プロパティによって決定される形式で表示されます。これはListsとProperty Listsで説明されています。しかし、オブジェクトはスクリプトの一部としてasText
関数のハンドラを実装することで、好きな方法でテキスト文字列として自己を表現することができます。以下のようになります:
to handle asText
return "Part number " & my partNum
end asText
オブジェクトがasText
メッセージを処理しないが、asText
プロパティを持っている場合、そのプロパティの値がオブジェクトの文字列表現として使用されます。これはスクリプトを全く持っていないかもしれない一部のシンプルなオブジェクトにとって非常に便利です。
さらに柔軟性を持たせるために、オブジェクトがasText
ハンドラもasText
プロパティも持っていないが、asTextFormat
プロパティを持っている場合、そのプロパティの値はmerge()関数によって評価され、オブジェクトのテキスト表現を提供します。これにより、オブジェクトの他のプロパ ティの値を組み合わせたよりダイナミックな解決策が提供されます:
set account to {type:"Savings", balance:1234.25,
asTextFormat:"[[my type]] Account: [[my balance]]"}
put account -- "Savings Account: 1234.25"
add 5050.50 to account's balance
put account -- "Savings Account: 6284.75"