掲示板。そう、掲示板です。なんだか、いたずらされていますね。対処をしたいとは思うのですが、この掲示板はプロバイダが提供しているものなので対処のしようがなかったり。まぁ、基本的にはほっておきます。あまりにも続くようなら、こういう輩を相手に時間を潰すのもなんですので、あっさりと掲示板を閉じます。手軽ではないですが、なにかあればメールの方でご連絡を。GMail の blackcharan さん宛に。今後、メールも GMail に統一させようと思っています。PHP の勉強がてらに掲示板を作るという手もあるな。そうすれば、嫌いな人...もとい、嫌がらせをする人のフィルタリングも思いのまま。
以上、業務連絡でした。
Database Events は終わって、デザインパターンに戻ります。デザインパターンの話って今年中に終わるのかしらん。今回は、Template Method を取り上げます。。デザインパターンの中では比較的単純なパターンだそうです。
Template Method は、『決まった処理手順をテンプレートにし、処理手順の一部だけを置き換えやすくする』デザインパターンです。既存のハンドラを組み合わせて定型的な処理を行う AppleScript にはある意味お似合いのデザインパターンかな。
例えば、Finder で選択している項目に対してなんらかの処理を施したいというスクリプト。画像ファイルを回転したい。画像ファイルを縮小したい。画像ファイルのフォーマットを変更したい...。以下のような感じでしょうか?
property extensionList : {"jpg", "jpeg", "tif", "tiff", "png"}
tell application "Finder"
set curSelection to selection
if curSelection is {} then return
set imageFiles to {}
repeat with thisItem in curSelection
if (class of thisItem) is document file and (name extension of thisItem) is in extensionList then
set end of imageFiles to thisItem as alias
end if
end repeat
my shrinkingImages(imageFiles, 0.8)
beep 2
end tell
on shrinkingImages(imageFiles, percentage)
tell application "Image Events"
launch
repeat with thisImage in imageFiles
try
set imageRef to open thisImage
scale imageRef by factor percentage
save imageRef
close imageRef
on error
try
close imageRef
end try
end try
end repeat
end tell
end shrinkingImages
Finder で選択している画像に対して縮小、拡大を行います。上書き保存するので大事な画像に使ってはいけません。では、画像をトリミングするスクリプトを。
property extensionList : {"jpg", "jpeg", "tif", "tiff", "png"}
tell application "Finder"
set curSelection to selection
if curSelection is {} then return
set imageFiles to {}
repeat with thisItem in curSelection
if (class of thisItem) is document file and (name extension of thisItem) is in extensionList then
set end of imageFiles to thisItem as alias
end if
end repeat
my rotateImages(imageFiles, -90) -- *
beep 2
end tell
on rotateImages(imageFiles, degree)
tell application "Image Events"
launch
repeat with thisImage in imageFiles
try
set imageRef to open thisImage
rotate imageRef to angle degree
save imageRef
close imageRef
on error
try
close imageRef
end try
end try
end repeat
end tell
end rotateImages
画像を処理するハンドラが変更されました。Finder で処理をしている部分は同じものを使い回しています。実際はもう少し使いやすいように処理した画像を別名で保存したりするのですが、今回はデザインパターンを理解するためのものなので手を抜いています。
AppleScript は、上記のような決まった手順の処理というのが多いです。処理内容は同じで対象アプリケーションが変わっただけとか。こういうときに Template Method パターンを使えばしあわせになれるかもしれません。Template Method パターンでは、まず処理手順のテンプレートを作ります。処理手順のテンプレートというのは、ハンドラ呼び出しの組み合わせです。テンプレートは抽象クラス(AbstractClass。AppleScript にはクラスの概念がないですが、抽象オブジェクトというのもなんなので...)で作り、処理手順の一部を定義します。処理手順の一部というのはハンドラですね。
Template Method では処理のテンプレートは、親オブジェクトで定義し、変更されません。子オブジェクトで変更するのは、個々の処理だけです。処理手順は全ての子オブジェクトで使い回されます。テンプレートたる所以ですね。
上記のスクリプトでは、ファイルの選別とファイルの加工という処理があり、選別、加工と処理手順の流れがあります。
script FileProcessor
property extensionList : {}
on selectItems()
tell application "Finder"
set curSelection to selection
if curSelection is {} then return
set theList to {}
repeat with thisItem in curSelection
if (class of thisItem) is document file and (name extension of thisItem) is in extensionList then
set end of theList to thisItem as alias
end if
end repeat
return theList
end tell
end selectItems
on processItems(theseItems)
end processItems
on run
set theseItems to my selectItems()
my processItems(theseItems)
end run
end script
このような抽象的なオブジェクトを作ります。run ハンドラを templateMethod にしています。templateMethod では、自身で定義されている個別の処理を順番に呼び出します。selectItems() と processItems() が個別の処理になります。前者がファイルの選別を行い、後者で加工を行います。これらの個別の処理はこのスクリプトオブジェクトを継承したスクリプトオブジェクトで実装します。そうすることによって処理の一部だけを変更することが可能になります。
selectItems() ハンドラは既に実装しています。こういう変わらない処理なら、抽象クラスの方で実装しておく方がより楽になります。気に入らなければ継承先で上書きすればいいだけなので。以下のようにこのスクリプトオブジェクトを継承して使います。
script ShrinkingImagesProcessor
property parent : FileProcessor
property extensionList : {"jpg", "jpeg", "tif", "tiff", "png"}
property percentage : 0.5
on processItems(theseItems)
if theseItems is {} then return
tell application "Image Events"
launch
repeat with thisImage in theseItems
try
set imageRef to open thisImage
scale imageRef by factor percentage of me
save imageRef
close imageRef
on error
try
close imageRef
end try
end try
end repeat
end tell
end processItems
on setPercentage(num)
set percentage of me to num
end setPercentage
on getPercentage()
return percentage of me
end getPercentage
end script
tell ShrinkingImagesProcessor to run
画像の回転も同じように継承を行い変更したい処理手順を上書きするだけです。
script RotateImagesProcessor
property parent : FileProcessor
property extensionList : {"jpg", "jpeg", "tif", "tiff", "png"}
property degree : 90
on processItems(theseItems)
if theseItems is {} then return
tell application "Image Events"
launch
repeat with thisImage in theseItems
try
set imageRef to open thisImage
rotate imageRef to angle degree of me
save imageRef
close imageRef
on error
try
close imageRef
end try
end try
end repeat
end tell
end processItems
on setDegree(num)
set degree of me to num
end setDegree
on getDegree()
return degree of me
end getDegree
end script
tell RotateImagesProcessor to run
Template Method パターンを用いれば、既にある処理は使い回すことができます。また、各手順は個別に作成しているので修正も容易になります。以前に『どうせなら』という記事で書いた Livedoor Reader のログインスクリプトなどに Template Method を適用すれば、mixi や Gmail などのログインスクリプトも差分だけを書くことで流用することができます。
もちろん、問題がないわけでもありません。Template Method の問題点は、継承の継承を行っていくことでどのオブジェクトのどのハンドラが呼び出されているかが分かりにくくなるということです。以下のような感じです。
script FileProcessor
property extensionList : {}
on selectItems()
tell application "Finder"
set curSelection to selection
if curSelection is {} then return
set theList to {}
repeat with thisItem in curSelection
if (class of thisItem) is document file and (name extension of thisItem) is in extensionList of me then
set end of theList to thisItem as alias
end if
end repeat
return theList
end tell
end selectItems
on processItems(theseItems)
end processItems
on run
set theseItems to my selectItems()
my processItems(theseItems)
end run
end script
script ScriptFileProcessor
property parent : FileProcessor
property extensionList : {"scpt", "scptd", "applescript"}
on processItems(theseItems)
repeat with thisItem in theseItems
tell application "Finder" to open thisItem
end repeat
end processItems
end script
script JPEGFileProcessor
property parent : FileProcessor
property extensionList : {"jpg", "jpeg"}
on processItems(theseItems)
if theseItems is {} then return
tell application "Image Events"
launch
repeat with thisImage in theseItems
try
set imageRef to open thisImage
rotate imageRef to angle 90
save imageRef
close imageRef
on error
try
close imageRef
end try
end try
end repeat
end tell
end processItems
end script
script TIFFFileProcessor
property parent : JPEGFileProcessor
property extensionList : {"tif", "tiff"}
end script
tell TIFFFileProcessor to run
最後の TIFFFileProcessor が FileProcessor を継承している JPEGFileProcessor を継承しています。ここでは個別の処理をそれぞれで定義していませんが、こういう継承を行っているとどのオブジェクトのどのハンドラを呼び出しているのかということが分かりにくくなってきます。バグが見つけにくい、と。あまり深い継承は行わない方がいいようです。
Template Method パターンは、Factory / Factory Method パターンと深い関係があるそうで。オブジェクトの生成処理に Template Method パターンを適用することができますし、Template Method パターンで作ったオブジェクトのどれを利用するかを Factory を使って動的に変更することが可能になります。
デザインパターンは一つだけでなく複数を組み合わせることでさらに威力を発揮するということですね。
0 件のコメント :
コメントを投稿