スクリプトオブジェクト

スクリプトオブジェクトについておさらい

 前回、外部のスクリプト(オブジェクト)を読み込んで属性や変数に代入できることを説明しました。
 その、変数に対して属性や命令を指定できます。
 これはアプリーションが持っているオブジェクトと非常に近い使い方です。
 前回も書いた通り、これを「スクリプトオブジェクト」といいます。

 AppleScriptは、現在実行中のスクリプト自身を示す「me」や「my」と言った特別な変数を用意しています。
 これは「ハンドラで受け止める」で説明したように、「利用者定義命令 of me」や「my 利用者定義命令」などとしてすでに使っていますし、前回も少し使いました。
 皆さん「利用者定義命令 of me」が「利用者定義命令 of 現在のスクリプトオブジェクト」という意味だという雰囲気は、察していたことだと思います。

スクリプトオブジェクトを作る

 use文やload script命令で外部から読み込む他にも、スクリプトの中でスクリプトオブジェクトを作れます。
 このスクリプトオブジェクトは自動的にスクリプトオブジェクトと同じ名前のスクリプト属性に代入されると考えていいでしょう。
 つまり、名前を指定すれば、スクリプトのどこからでも使えます。書式は次のとおりです。

script スクリプトオブジェクト名
	-- スクリプト
end script

 スクリプトオブジェクト名の命名規則は変数と同じです。
 本書では、単語の頭をすべて大文字にし、後は小文字にして書きます。

 scriptend scriptの間のブロックには、今までと全く同じようにスクリプトを書けます。
 例えば次のように記述します。

tell ScriptObj to run

script ScriptObj
	display dialog "スクリプトオブジェクトです"
end script

 ハンドラを特に書かない場合は、スクリプトは自動的にrunハンドラに書かれたことになります。
 scriptブロックにあること以外は、今まで作ってきたスクリプトと同じです。

スクリプトオブジェクトの使い方

 スクリプトオブジェクトの特徴の一つは、多くの利用者定義ハンドラをまとめて書ける点です。
 ハンドラが多くなってくると、スクリプトがわかりづらくなります。
 そこでスクリプトオブジェクトを使えば、ハンドラをまとめて読みやすく整理できるというわけです。

tell ScriptObj
	run
	x()
end tell

script ScriptObj
	display dialog "runハンドラ"
	on x()
		display dialog "xハンドラ"
	end x
end script

 このスクリプトは、スクリプトオブジェクトの中にrunハンドラと、xという利用者定義ハンドラを一緒に書いています。
 さらに多くのハンドラを併用することも可能です。

 今まではハンドラを呼び出すにはx()と書けば良かったのに、スクリプトオブジェクトにまとめるとx() of ScriptObjと、スクリプトが長くなりって面倒だと感じるかもしれません。
 しかし、複雑なスクリプトになるほどスクリプトオブジェクトの良さが実感できます。

属性を加えてオブジェクトらしく

 スクリプトオブジェクトは「オブジェクト」の一種なので、ハンドラだけではなく属性を持つこともできます。
 書式は前回紹介したものと同じです。on script〜end scriptブロックの中に書けば、そのスクリプトオブジェクトの中だけで有効になります。

property 属性 : 初期値

 スクリプトオブジェクトの属性はレコードと似ています。

script ScriptObj
	property a_:0
	property b_:2
end script

a_ of ScriptObj
	--> 0
tell ScriptObj
	-- tellブロックでもofを付ける必要があります
	b_ of ScriptObj
end tell

 こように、まるっきりレコードと同じような使い方ができます。
 もちろんレコードと違って、スクリプトオブジェクトはオブジェクトですから、属性と同時にハンドラも設定しないと意味がありませんけど。

 スクリプトオブジェクト内部での属性の使い方は、前回説明した通りです。
 違いはscriptブロックに囲まれてることだけです。

script ScriptObj
	property a_ : 0
	property b_ : 2
	
	display dialog "a_ : " & a_ & return & "b_ : " & b_
end script

tell ScriptObj to run

属性用のインタフェースを作る

 スクリプト属性は外部から簡単に読み書きが可能ですが、オブジェクトの外から読み書きされると困るタイミングが往々にして生じます。
 そこで、読み書き用にハンドラを作っておいて、そちらを利用するようにして、スクリプトに変な動きが出ないようにします。

 他のオブジェクト指向言語では、オブジェクトの外部から自由に属性の値を書き換えられないようにする仕組みが用意されているのですが、AppleScriptは読み書きし放題です。
 一見便利なようですが、オブジェクトの中だけで処理が完結できなくなってしまうため、最終的にはスクリプトが読みにくくなってしまうことも少なくありません。

script ScriptObj
	property a_ : 0
	
	on getA()
		return a_
	end getA
	
	on setA(a)
		set a_ to a
	end setA
end script

 ただめんどくさくなっただけのように感じるかもしれませんが、このように属性の読み書き用のハンドラを作っておくことで、アプリケーションオブジェクトのように属性を書き換えた時に作用があるようにすることもできます。
 さらに、条件式を入れることでより柔軟な結果を返すこともできるようになります。

 このようなハンドラを書いたら、絶対に外部から直接に属性を書き換えることをしては行けません。
 直接書き換えるとせっかく書いた外部とのインタフェース(注1)用のハンドラの意味がなくなってしまいます。
 いくつもスクリプトオブジェクトを作るうちに、このような一見遠回りの書き方が、なるほど便利だと思うはずです。

インタフェースを制限する

 スクリプト属性と同様にハンドラも外部から簡単に使うことができますが、オブジェクトの外から使う必要の無いハンドラも、往々にして存在します。
 ところが、AppleScriptにはハンドラのスクリプトオブジェクト外部からの使用を禁止する仕組みが用意されていません。
 要するに、制限したくてもできないのです。

 そんなわけで、AppleScriptではハンドラのコメントに「外から使わないでほしい」と書くていどの対処しかできません。
 本書ではJavaなどのオブジェクト指向言語で使われている「公開(public)」と「非公開(private)」をハンドラの後ろに書いておくことにします。
 全く文法的には意味のないことですから、書いたからと言って動作が変わるものでもないですが、オブジェクト指向は心がけが肝心です(注1)。

script ScriptObj
	on x() -- 公開(public)
		return y(5)
	end x
	
	on y(n) -- 非公開(private)
		return n *2
	end y
end script

 制限すると、やれることが減って不便じゃないかと思われるかもしれませんが、例えば、自動車が歯車の位置や燃料噴射量・電圧などを直接操作することで動くとしたら、そんなのは不便なだけです。
 そういう機械に近くて分かりにくいことはボンネットの中に隠しておいて、利用者は座席の前についている簡単な操作系(インタフェース)を使うだけのほうが、優れているということは、すぐに理解できるかと思います。
 スクリプトを書く場合も同じで、操作系は極力少なくすることが大切です。

「ICloudObj」を作ってみる

 例えば、「iCloud」の操作をスクリプトオブジェクトを使ってまとめてみましょう。

 iCloudというOSの便利な機能があるのに、AppleScriptから使おうとすると、ほとんど何も用意されていません。
 そこで、スクリプトオブジェクトというまとまりで、欲しい機能をまとめます。
 一旦まとめてしまえば、アプリケーション感覚で使えるようになります。

-- 「iCloud」をオブジェクトとしてラッピング
script ICloudObj
	-- 「iCloud Drive」関連のパス
	property LIBRARY_PATH : POSIX path of (path to library folder from user domain)
	property ICLOUD_PATH : LIBRARY_PATH & "Mobile Documents/"
	
	(*
	 * アプリケーションの書類フォルダへのパスを得る
	 * theApplication : アプリケーションオブジェクト
	 * 返り値 : 書類フォルダのPOSIX path
	 *)
	on getDocumentFolderPath(theApplication as application) -- 公開(public)
		set theId to id of theApplication
		set theFolderName to replaceText(theId, ".", "~")
		return ICLOUD_PATH & theFolderName & "/Documents/"
	end getDocumentFolderPath
	
	-- 文字列の置換
	on replaceText(theText as text, serchStr as text, replaceStr as text) -- 非公開(private)
		set tmp to AppleScript's text item delimiters
		set AppleScript's text item delimiters to serchStr
		set theList to every text item of theText
		set AppleScript's text item delimiters to replaceStr
		set theText to theList as string
		set AppleScript's text item delimiters to tmp
		return theText
	end replaceText
end script

外部スクリプトとして使ってみる

 スクリプトオブジェクトは役割上、スクリプトが長くなりがちですし、まとまりが良いので呼び出して使いやすくなります。
 そこで、大抵はスクリプトライブラリに保存して、あちこちのスクリプトから使うことになります。

 ICloudObjを書いたスクリプトを "iCloudUtil"というファイル名でスクリプトライブラリに保存した場合、次のような形で呼び出せます。

tell ICloudObj of script "iCloudUtil"
	getDocumentFolderPath(application "Script Editor")
end tell

 もちろん、こういう書き方でも構いません。

getDocumentFolderPath(application "Script Editor") of ICloudObj of script "iCloudUtil"

 useを使ってもいいです。

use iCloudUtil : script "iCloudUtil"
getDocumentFolderPath(application "Script Editor") of ICloudObj of iCloudUtil

スクリプト属性を設定しよう」でやった、外部スクリプトの使い方のオブジェクト階層が深くなっただけですね。
 最初に説明した通り、AppleScriptのスクリプトは全てスクリプトオブジェクトなので、外部ファイルとして使う場合、あえて階層を深くする必要があることは少ないので、scriptブロックは外してしまうことが多いかと思います。

【今回のまとめ】

スクリプトオブジェクトは、

  • ハンドラと属性をまとめた値
  • 定義は、scriptとend scriptの間にスクリプトを書くだけ
  • アプリケーションオブジェクトと同様に「tell」で指定可能

オブジェクトを使う時は、

  • 外部から属性を直接操作することを避ける
  • 外部から操作できるハンドラも極力減らす
  • ただし制限する仕組みが無いので工夫で対処