スクリプトオブジェクトを理解してみる

load script 命令はもしかすると難しいものなのかもしれません。なぜかというと、スクリプトオブジェクトの存在が理解を阻むからです。しかし、load script 命令はスクリプトオブジェクトを返します。「load script 命令におけるスクリプトの再利用」では触れませんでしたが、今回はその辺りのことを。

AppleScript ではオブジェクトが全てです。値という言葉を使うことはありますが、「値 = オブジェクト」とほぼ同意です。AppleScript はオブジェクト単位で考えると理解しやすいかもしれません。

もちろん、スクリプトファイルに保存されたスクリプトもオブジェクトです。

Script Editor で開く

set documents_folder to path to documents folder
tell application "Finder"
    activate
    set the_window to make new Finder window
    set target of the_window to documents_folder
end tell

このようなスクリプトでもオブジェクトです。AppleScript では Java や Objective-C、Python、PHP などの他のプログラム言語と異なり「クラス」を作りません。AppleScript でクラスを定義できるのは、操作対象となるアプリケーションや AppleScript だけです。AppleScript でユーザーが作ることができるのは、「オブジェクト」だけです。

ユーザーが定義したオブジェクトのことを「スクリプトオブジェクト(Script Object)」といいます。スクリプトオブジェクトはデータ(属性)とアクション(メソッド。AppleScript ではハンドラ)を持つことができます。

通常、スクリプトオブジェクトは予約語 script を用いて定義します。

script valiable_name
    [ property parent: reference_of_parent ]
    [ property property_label: initial_value ]...
    [ handler_definition ]...
    [ statement ]...
end script

script から end script の間までがスクリプトオブジェクトの定義になります。valiablename はスクリプトオブジェクトの名前になります。parent 属性で指定する referenceof_parent は、スクリプトオブジェクトの親の参照です。他のスクリプトオブジェクトから属性やハンドラを継承したいときに利用します。

propertylabel は 属性の識別子で、initialvalue に初期値を記述します。handlerdefinition はハンドラ(アクション)の定義で、statement は run ハンドラに含まれるものになります。valiablename 以外は全て任意で、あってもなくてもかまいません。

だから、一番シンプルなスクリプトオブジェクトは、valiable_name だけを指定したものになります。

script ScriptObject
end script

しかし、このままでは何の役にも立ちません。他の言語のように後から属性を追加してデータを加えるということはできません。通常は属性とハンドラを定義し、それなりの体裁を整えておきます。

Script Editor で開く

script FirstScriptObject -- スクリプトオブジェクトの名前
    -- スクリプトオブジェクトの属性。初期値は空のリスト
    property the_list : {}

    on add_data(x) -- スクリプトオブジェクトのハンドラ
        set end of the_list to x
    end add_data

    -- スクリプトオブジェクトの任意の文(暗黙の run ハンドラに含まれる)
    add_data(10)
    add_data(20)
    add_data(30)
    the_list
end script

run FirstScriptObject
--> {10, 20, 30}

AppleScript では、トップレベルのスクリプトオブジェクト(Top-Level Script Object)から処理が開始されます。どのスクリプトにもトップレベルのスクリプトオブジェクトというものが存在していますが、それはユーザーの目には触れません。

トップレベルのスクリプトオブジェクトは、ユーザーが定義するものではありません。AppleScript のスクリプトに既に存在しているものです。トップレベルのスクリプトオブジェクトは決して明示されませんが、全てのスクリプトはトップレベルのスクリプトオブジェクトに含まれたものになります。

set documents_folder to path to documents folder
tell application "Finder"
    activate
    set the_window to make new Finder window
    set target of the_window to documents_folder
end tell

このスクリプトならトップレベルのスクリプトオブジェクトに属する run ハンドラの中のにあるスクリプトになります。言い換えるなら、ユーザーは意識していなくてもスクリプトオブジェクトを作成しているのです。上記のスクリプトは分かりやすく書くと次のようになっています。

(* script *) -- トップレベルのスクリプトオブジェクト
    (* on run *) -- トップレベルのスクリプトオブジェクトの run ハンドラ
        set documents_folder to path to documents folder
        tell application "Finder"
            activate
            set the_window to make new Finder window
            set target of the_window to documents_folder
        end tell
    (* end run *)
(* end script *)

擬似的なスクリプトですが、先のスクリプトはコメントアウトしている部分が暗黙のうちに追加されているのです。

トップレベルのスクリプトオブジェクトは普段は意識しないので感覚がつかみにくいかもしれませんが、トップレベルのスクリプトオブジェクトは全てのスクリプトオブジェクトの親になります(AppleScript では継承の関係を「親」と「子」で表現します)。また、トップレベルのスクリプトオブジェクトも親を持っています。

Script Editor で開く

script FirstObject
end script

me -- トップレベルのスクリプトオブジェクト
--> «script» 

-- FirstObject の親は?
parent of FirstObject
--> «script», トップレベルのスクリプトオブジェクト

-- トップレベルのスクリプトオブジェクトの親は?
set parent_object to parent of me
--> «script AppleScript», AppleScript 自身(AppleScript Component )

--> AppleScript の親は?
parent of parent_object
--> current application, スクリプトを実行しているアプリケーション

current application(スクリプトを実行しているアプリケーション)が最上位の親になります。AppleScript では下図のような継承関係が定義されています。

AppleScript Inheritance Chain

AppleScript でなんらかの命令をオブジェクトに送るとき、通常は「ターゲット(命令を処理するオブジェクト)」に対して命令が送られます。ターゲットで命令を処理できない(命令が定義されていない)場合、この継承関係をたどりながら命令が定義されているオブジェクトを探索します。

次のスクリプトがエラーにならないのは、最終的に Script Editor に問い合わせを行っているからです。

Script Editor で開く

-- トップレベルのスクリプトオブジェクトで定義された x ハンドラ
on x()
    -- 2. document という用語はスクリプトオブジェクトで定義されていない
    -- 3. AppleScript Component を探すが、見つからない
    -- 4. current application(Script Editor)で探し、見つかる
    get name of front document
end x

script FirstObject
    on run
        -- ハンドラ x は、ThirdObject では定義されていない
        -- 1. トップレベルのスクリプトオブジェクトを探しにいく
        x()
    end run
end script

run FirstObject
-- 5. "名称未設定" という結果が得られる

Script Editor 上でスクリプトを実行しているとき、current application(スクリプトを実行しているアプリケーション)は Script Editor になります。Script Editor では document という用語が定義されています。結果、スクリプトが記述されているドキュメントの名称が返されるのです。ちなみにこのスクリプトをアプリケーションとして保存し、実行するとエラーになります。

ユーザーが定義したスクリプトオブジェクトは、他のスクリプトオブジェクトを親に指定し、親の持っている属性やハンドラを継承することができます。親が指定されていない場合、先にも書いたようにトップレベルのスクリプトオブジェクトが自動的に親になります。

Script Editor で開く

script FirstObject
    on greeting(your_name)
        log "FirstObject's greeting"
        return "Hello, " & your_name
    end greeting
end script

script SecondObject
    property parent : FirstObject -- 親を指定
end script

greeting("Mac OS X") of FirstObject
--> "Hello, Mac OS X"
greeting("Mac OS 9") of SecondObject -- 親のハンドラを利用
--> "Hello, Mac OS 9"

FirstObject で greeting を定義せず、トップレベルのスクリプトオブジェクトで定義してみましょう。

Script Editor で開く

on greeting(your_name)
    log "Top-level Script Object's greeting"
    display dialog "Hello, " & your_name
end greeting

script FirstObject
    -- greeting は定義されていないのでトップレベルのスクリプトオブジェクトを探索
end script

script SecondObject
    property parent : FirstObject -- 親を指定
end script

greeting("Mac OS X") of FirstObject
--> "Hello, Mac OS X"
greeting("Mac OS 9") of SecondObject -- 親のハンドラを利用
--> "Hello, Mac OS 9"

parent で親を指定しないスクリプトオブジェクトでは自身にハンドラが定義されていない時、トップレベルのスクリプトオブジェクトで定義されているかどうかを調べます。トップレベルのスクリプトオブジェクトで定義されていない場合、AppleScript Component で定義されていないかどうか調べます。AppleScript Component でも定義されていない場合、current application で定義されているかどうか調べます。current application でも定義されていない場合、エラーになります。

スクリプトオブジェクトで親を指定している場合、親のスクリプトオブジェクトを経由しながらハンドラを探索しますが、最終的には current application まで辿っていきます。

このように全てのスクリプトオブジェクトがトップレベルのスクリプトオブジェクトを経由するということが分かっているなら、エラーなどをトップレベルのスクリプトオブジェクトに集約することができます。

Script Editor で開く

on _error(m, n)
    display dialog {n, return, return, m} as text
end _error

on run
    try
        -- 何らかの処理
        error number 0 -- わざとエラーを起こしてみる
    on error m number n
        _error("トップレベルのスクリプトオブジェクトでエラー発生", n)
        run FirstObject
        x()
    end try
end run

script FirstObject
    script SecondObject
        on run
            try
                -- 何らかの処理
                error number 2 -- わざとエラーを起こしてみる
            on error m number n
                _error("SecondObject でエラー発生", n)
            end try
        end run
    end script

    on run
        try
            -- 何らかの処理
            error number 1 -- わざとエラーを起こしてみる
        on error m number n
            _error("FirstObject でエラー発生", n)
            run SecondObject
        end try
    end run
end script

on x()
    try
        -- 何らかの処理
        error number 3 -- わざとエラーを起こしてみる
    on error m number n
        _error("x ハンドラでエラー発生", n)
    end try
end x

まぁ、あまり利用しないテクニックですが。

スクリプトオブジェクトで親を指定するときに大事なのは、それが誰のものかを明示することです。

Script Editor で開く

script FileFilter
    property file_extensions : {}

    on filter(this_item)
        tell application "Finder"
            if (class of this_item) is not document file then return false
            -- of me(もしくは my)で誰の file_extensions か明示している
            -- of me(もしくは my)がないとこの判定は失敗する
            return name extension of this_item is in file_extensions of me
        end tell
    end filter
end script

script ImageFileFilter
    property parent : FileFilter
    property file_extensions : {"jpg", "jpeg", "png", "gif", "pict", "tiff", "tif"}
end script

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return
    set image_files to {}
    repeat with this_item in selected_items
        if ImageFileFilter's filter(this_item) then
            set end of image_files to this_item as alias
        end if
    end repeat
    image_files
end tell

AppleScript には me(もしくは my)や it といったオブジェクトを指し示す予約語があります。これらの違いが分からない、と時々耳にします。me は「現在実行されているスクリプトオブジェクト」を指し、it は「現在のターゲット」を指します。

Script Editor で開く

set the_list to {10, 20, 30}

tell the_list
    it -- {10, 20, 30}
    me -- «script»

    class of it -- list
    class of me -- script

    -- 命令は現在のターゲットに送られる
    count -- 3
end tell

it -- «script»
me -- «script»

tell application "Finder"
    it -- application "Finder"
    me -- «script»
end tell

script FirstObject
    me
end script

script SecondObject
    property parent : FirstObject
end script

run FirstObject
--> «script FirstObject»
run SecondObject
--> «script SecondObject»

それぞれ、文脈によって何を指し示しているかが変わってきます。

親を指定したスクリプトオブジェクトでは親と同じハンドラを持つことができます。

Script Editor で開く

script DebugLog
    on debug_message(msg)
        tell me
            activate
            display dialog msg buttons {"OK"} default button 1 with icon 1
        end tell
    end debug_message
end script

script CustomDebugLog
    property parent : DebugLog

    on debug_message(msg)
        set msg to ((current date) as text) & ": " & msg
        log (msg)
    end debug_message
end script

DebugLog's debug_message("DebugLog's logging")
CustomDebugLog's debug_message("CustomDebugLog's logging")

DebugLog ではダイアログでメッセージを表示しますが、子(CustomDebugLog)の方は現在の日時を追加し、Script Editor のイベントログに書き出すようにしています。親の(DebugLog)が持っているハンドラを上書き(オーバーライド)して、機能を修正/拡張したのです。

子は親の機能を拡張することができますが、continue を使って処理をそのまま親に任せてしまうこともできます(委譲といいます)。

Script Editor で開く

script DebugLog
    on debug_message(msg)
        tell me
            activate
            display dialog msg buttons {"OK"} default button 1 with icon 1
        end tell
    end debug_message
end script

script CustomDebugLog
    property parent : DebugLog

    on debug_message(msg)
        set msg to ((current date) as text) & return & return & msg
        continue debug_message(msg) -- 親に処理を丸投げ
    end debug_message
end script

DebugLog's debug_message("DebugLog's logging")
CustomDebugLog's debug_message("CustomDebugLog's logging")

AppleScript ではハンドラの上書きというより、横取りといった方があっていると思うのですが...。例えば、警告音を鳴らす beep 命令をカスタマイズ(横取り)することができます。

Script Editor で開く

on beep
    log "beep"
end beep

beep

このスクリプトを実行しても警告音は鳴りません。警告音を鳴らすには continue で処理を委譲する必要があります。横取りしたものを元に返すのです。

Script Editor で開く

on beep
    log "beep"
    continue beep
end beep

beep

quit ハンドラで continue quit とするのは終了命令を横取りしたままになり、終了しないアプリケーションになるのでアプリケーションに終了命令を返しているのです。

Script Editor で開く

on quit -- 終了命令を横取り
    -- アプリケーションに終了命令を渡さないと終了しないアプリケーションになる
end quit

このスクリプトを「実行後、自動的に終了しない」アプリケーションで保存して、実行すると終了させても終了できないアプリケーションになります。quit ハンドラがあるときは次のように記述しておきます。

Script Editor で開く

on quit -- 終了命令を横取り
    -- アプリケーションに終了命令を渡さないと終了しないアプリケーションになる
    (* 終了前の処理が入る *)
    continue quit
end quit

スクリプトオブジェクトはスクリプト実行時に初期化され、作成されます。トップレベルのスクリプトオブジェクトは run ハンドラ実行時に初期化されます。スクリプトオブジェクトの初期化のタイミングは命令を受け取る直前です。この性質を利用すると異なる初期値を持つスクリプトオブジェクトを作成することができます。

Script Editor で開く

on FileFilter(extension_list)
    script FileFilter
        property file_extensions : extension_list

        on filter(this_item)
            return name extension of this_item is in file_extensions of me
        end filter
    end script
end FileFilter

set ImageFileFilter to FileFilter({"jpg", "jpeg", "png", "gif", "pict", "tiff", "tif"})

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return
    set image_files to {}
    repeat with this_item in selected_items
        if (class of this_item) is document file then
            if ImageFileFilter's filter(this_item) then
                set end of image_files to this_item as alias
            end if
        end if
    end repeat
    image_files
end tell

このようにして作成されたスクリプトオブジェクトは、たとえ初期値が同じものであっても異なるスクリプトオブジェクトになります。

Script Editor で開く

on objectMaker()
    script
    end script
end objectMaker

set obje_A to objectMaker()
set obje_B to objectMaker()
obje_A is obje_B
--> false

ちなみに set 命令ではオブジェクトは共有されます。

Script Editor で開く

set the_list to {1, 2, 3}
set new_list to the_list
set end of new_list to 4
the_list
--> {1, 2, 3, 4}

これは対象がスクリプトオブジェクトでも同じで、オブジェクトのコピーが欲しい場合は copy 命令を使います。

Script Editor で開く

on objectMaker()
    script
    end script
end objectMaker

set obje_A to objectMaker()

-- set ではオブジェクトは共有される
set obje_B to obje_A
obje_A is obje_B
--> true

-- 複製が欲しいときは copy 命令を使う
copy obje_A to obje_B
obje_A is obje_B
--> false

駆け足ですが、AppleScript やスクリプトオブジェクトがどのような性質を持っているか理解できた...でしょうか?

できねえよ。

...分かっています。全ての責は私の理解不足/文章力足らずに帰します。分からない、理解できない、もっとここを説明してほしい...という部分があればコメント(設置してみました)からお願いします。

load script 命令におけるスクリプトの再利用

前回までの AppleScript Programming Tips を読み返していて...こりゃだめだと反省。リファレンスはいらないですね。ということでリファレンスじみたことはやめます。すいませんが記事を修正、削除しました。リファレンスの方は別にまとめようかと画策中です。今回は load script 命令。run scriptstore script とくれば、load script 命令にも触れないわけにはいきません。

load script 命令というのは既にあるスクリプトを読み込んでスクリプトオブジェクトとして利用するための命令...言い換えると、既存のスクリプトを使い回して楽をするために必須の命令です。

少し、話が横道にそれますが...基本的には AppleScript のスクリプトって使い捨てだと思います。動けばそれでいいし、目的を達成できればそれで十分です。多くの人にとって AppleScript はそういうもので、既存のスクリプトを使い回すという概念があまり浸透していないように思えます。

多くのスクリプトが使い回すことを前提に組み立てられていません。多くのスクリプトは以下のような感じではないでしょうか。

Script Editor で開く

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return
    try
        repeat with this_item in selected_items
            set locked of this_item to not (locked of this_item)
        end repeat
    end try
end tell

Finder で選択している項目のロックを ON/OFF するスクリプトです。直線的なスクリプトです。もちろん、これでいいですし、エラー処理はしていませんがきちんと動きます。私の環境でもこのようなスクリプトが大部分を占めています。

AppleScript のいいところは、このような気楽な書きなぐりのようなスクリプトで十分目的を達成できるというところです。目的を達成できれば、これ以上スクリプトを保守/管理する気は起きません。次にこのスクリプトを開いて中身をみるのはいつになることか...。

また、AppleScript というのは個人の環境に強く依存するプログラム言語です。私に必須のスクリプトが必ずしもあなたに必須のスクリプトではないのです。なぜなら、AppleScript がアプリケーションを操作するための言語だからです。私は OmniOutliner を使ってこの書類を書いています。しかし、あなたは違うでしょう。私が何らかのアプリケーションを欲するとき、AppleScript に対応しているかどうかを気にかけますが、あなたはそうではないでしょう。

個人の環境に強く依存する AppleScript は、同時にスクリプトの書き方にも影響しています。

ハンドラの記述の仕方、参照の記述の仕方、命令の使い方、文字列の組み立て方、繰り返しの使い方、変数の命名方法、グローバル変数を多用するかどうか、ローカル変数を明示するかどうか等々...、様々なスクリプトを見てきましが、多種多様です。

AppleScript は同じことでも複数の書き方ができるため個性的なスクリプトが多いです。個性的なスクリプトを記述する人ほど、他人のスクリプトをそのまま使うことに忌避感を覚えます。このことが、使い回せるスクリプト(ライブラリ、モジュール)が公開されても利用されることがないことの遠因なのでは?思います。

駄法螺ですが。少なくとも自分自身にはそのような傾向があるかなと思います。

閑話休題。上記の Finder 項目のロックのトグル処理のスクリプトを開いて中身をみることがいつになるかは分かりませんが、Finder の選択項目に何らかの処理を施すスクリプトはそのうちにまた作ると思います。例えば、選択項目をゴミ箱に移動する、選択項目にラベルを付ける、選択項目の名前を変更する...。

スクリプトを使い回せるようにするにはどうすればいいのか、Finder の選択項目の処理を例に少し考えてみましょう。

Script Editor で開く

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return

    repeat with this_item in selected_items
        set label index of this_item to 1
    end repeat
end tell

このスクリプトは選択項目のラベルをオレンジ色にするスクリプトです。このスクリプトを見て、いくつかのことに気がつくと思います。

  1. 繰り返しの中身だけがロックのトグル処理のスクリプトと異なる
  2. 選択項目の処理というのは(エラー処理を入れていないものの)定型文だ

これらの部分です。スクリプト再利用の鍵となるのは処理の細分化です。個々の処理をハンドラで切り離せるかどうかを考えることがスクリプト再利用の第一歩です。まず、繰り返しの中身の処理はハンドラにすることができます。

Script Editor で開く

tell application "Finder"
    set selected_items to selection
    if selected_items is {} then return

    repeat with this_item in selected_items
        my label_orange(this_item)
    end repeat
end tell

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

on toggle_lock(this_item)
    tell application "Finder"
        set locked of this_item to not (locked of this_item)
    end tell
end toggle_lock

ロックのトグル処理もハンドラにしました。たかだか一行の処理をハンドラにすることもないのでは?と思うかもしれませんが、ハンドラとはこういうものなのです。では、「選択項目の処理というのは定型文」という部分を考えてみましょう。選択項目の処理に毎回毎回この定型文をコピペするなり、記述するなりするのでしょうか?

それはなんだか面倒だ、と思ったならこの部分もハンドラにしてしまってください。

Script Editor で開く

on process_Finder_selection()
    tell application "Finder"
        set selected_items to selection
        if selected_items is {} then return

        repeat with this_item in selected_items
            my label_orange(this_item)
        end repeat
    end tell
end process_Finder_selection

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

on toggle_lock(this_item)
    tell application "Finder"
        set locked of this_item to not (locked of this_item)
    end tell
end toggle_lock

単にハンドラにしただけです。ですが、これでは使い勝手が悪いです。繰り返しの部分を毎回書き変える必要があります。この部分を目的に応じてほかのハンドラに差し替えれるようにできれば、もう少し使い勝手がいいものになります。

Script Editor で開く

property action : missing value

on run
    set action of me to label_orange
    my process_Finder_selection()
    set action of me to toggle_lock
    my process_Finder_selection()
end run

on process_Finder_selection()
    tell application "Finder"
        set selected_items to selection
        if selected_items is {} then return

        repeat with this_item in selected_items
            my action(this_item)
        end repeat
    end tell
end process_Finder_selection

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

on toggle_lock(this_item)
    tell application "Finder"
        set locked of this_item to not (locked of this_item)
    end tell
end toggle_lock

これで個々のハンドラはそれぞれ単一の処理を行うようになりました。今のところ、このことがどのような意味を持っているのかが分かりにくいかもしれません。しかし、使い回すことを考えながらスクリプトを作っていると、その利点が分かってくることと思います。

上記のスクリプトをデスクトップに FinderUtility.scpt として保存しておきます。load script 命令を使ってスクリプトを読み込んでみます。

load script 命令は AppleScript ファイルを引数にとり、file 参照か alias で指定します。ファイルはコンパイル済みスクリプトかアプリケーション形式で保存したスクリプトファイルです。実行専用で保存されたアプリケーションでもかまいません。返り値はスクリプトオブジェクトです。

Script Editor で開く

set the_object to load script (choose file of type {"scpt", "app", "scptd"} without invisibles)
--> «script»

では FinderUtility.scpt を読み込み、選択項目の処理をしてみましょう。

Script Editor で開く

set the_folder to path to desktop as text
set the_file to the_folder & "FinderUtility.scpt"

set FinderUtility to load script file the_file

tell FinderUtility
    -- 選択項目のラベルをオレンジに
    set action of it to label_orange of it
    process_Finder_selection()
    delay 1
    -- 選択項目のラベルをなしに
    set action of it to label_none of me
    process_Finder_selection()
end tell

on label_none(this_item)
    tell application "Finder"
        set label index of this_item to 0
    end tell
end label_none

FinderUtility.scpt を読み込んで、action 属性に実際の処理を記述したハンドラを設定し、processFinderselection ハンドラを呼べばいいだけです。action 属性に設定するハンドラは別段どこのスクリプトにあってもかまいません。ここでは同一のスクリプト内に書いていますが、ハンドラだけを別のスクリプトに保存しておき load script 命令を使って読み込んだものでもかまいません。

ちなみに、ハンドラに引数としてハンドラを渡さないのはどうしてなのか?

Script Editor で開く

on run
    my process_Finder_selection(label_orange)
end run

on process_Finder_selection(action)
    tell application "Finder"
        set selected_items to selection
        if selected_items is {} then return
        repeat with this_item in selected_items
            try
                my action(this_item)
            on error m number n
                display dialog {n, return, return, m} as text
            end try
        end repeat
    end tell
end process_Finder_selection

on label_orange(this_item)
    tell application "Finder"
        set label index of this_item to 1
    end tell
end label_orange

これだとエラーになるからです。このエラーを回避するためにハンドラをいったんスクリプトオブジェクトの属性におさめ、その属性を参照するようにしています。スクリプトの実行時にハンドラを動的生成し、処理を切り替えるときによく使われるテクニックです。他にもハンドラにスクリプトオブジェクトを渡して処理を行うというのもよく使われるテクニックです。

Script Editor で開く

script FinderAction
    on do_action(this_item)
        tell application "Finder"
            (* 何らかの処理 *)
            log "Call do_action of FinderAction"
        end tell
    end do_action
end script

script SEAction
    on do_action(this_item)
        tell application "System Events"
            (* 何らかの処理 *)
            log "Call do_action of SEAction"
        end tell
    end do_action
end script

on run
    tell application "Finder"
        set selected_items to selection as alias list
        if selected_items is {} then return
        repeat with this_item in selected_items
            my do_action(this_item, SEAction)
            my do_action(this_item, FinderAction)
        end repeat
    end tell
end run

on do_action(this_item, this_object)
    this_object's do_action(this_item)
end do_action

このような使い方は AppleScript ではあまりしませんが、トリッキーなものではありません。知っていると、スクリプトでできることの幅が広がりますし、保守/管理も楽になります。