ろぐれこーど

限界組み込みエンジニアの学習記録とちょっぴりポエム

Template MethodパターンによるVBAマクロの実装

以下の本を読み、テンプレートメソッド(Template Method)パターンというデザインパターンを学びました。

せっかくなので、普段から使っているVBAのマクロをテンプレートメソッドで書いてみました。

テンプレートメソッド

テンプレートメソッドでは、(共通の前処理)→実処理→(共通の後処理) といった前後に定型処理が実施されるロジックの共通処理部分を切り出し、メソッド化するというものです。簡単なもので言うと、ファイルopen, closeやメモリ確保などリソース管理があります。

例としてファイルのopen, close処理を図で示すと以下のような構造になります。

f:id:gco46:20200512224242j:plain:w400

open, closeはファイルを操作する上で必要な処理であり、中身の処理は何でもよいです。テンプレートメソッドの最も簡易な形式としては、この中身の処理をコール側で指定してやることで実現できそうです。実際の開発では共通リソースを使用するインスタンス生成などに使用できそうです。詳しくは以下のサイトが参考になるかと思います。

qiita.com

qiita.com

多くの場合、オブジェクト指向言語デザインパターンが使用されるかと思いますが、C言語などでも実現できます(厳密にやると大変だと思いますが)。要はオブジェクトに応じて振る舞いが変わる、ポリモーフィズムが実現できればよいです。簡単に実現する方法の一つとして、コールバック関数が挙げられます。

VBAマクロでの典型的な前処理、後処理

VBAマクロでは実行速度を早くするために、以下のような前処理/後処理を入れることが多いです。

Private Sub startMacro()
'
' マクロ実行前処理(高速化のために各種処理を無効化)
'
    With Application
        .ScreenUpdating = False              '描画を省略
        .Calculation = xlCalculationManual   '手動計算
        .DisplayAlerts = False               '警告を省略
        .EnableEvents = False                'イベント無効
    End With
End Sub

Private Sub endMacro()
'
' マクロ実行後処理(各種処理有効化)
'
    With Application
        .ScreenUpdating = True                '描画する
        .Calculation = xlCalculationAutomatic '自動計算
        .DisplayAlerts = True                 '警告を行う
        .EnableEvents = True                  'イベント有効
    End With
End Sub

それぞれの意味についてはここでは省略します。

マクロを実行する前後にこの処理を実施し、実処理はコールバック関数として与えることで、VBAによるマクロのテンプレートメソッドを実現します。

コールバック関数実装

VBAでは関数ポインタを扱う仕組みがありません。そこでクラスモジュールにメソッドを定義し、CallByName()で外から呼び出す方法を取ります。詳しくは以下を参照。

qiita.com

実装した内容は以下です。

github.com

Callback.clsがマクロの実処理を記述したファイルです。Shortcuts.basで前処理/後処理を含んだテンプレートexecuteMacro()を実行することで、テンプレートメソッドパターンを実現しています。内容を一部抜粋します。

Private Sub executeMacro(method As String)
'
' マクロ実行関数
' method でコールバック関数を指定する
'
    Dim cbObj As New Callback
    startMacro
    executeCallback Array(cbObj, method)
    endMacro
End Sub

Private Sub executeCallback(cb_arr)
    CallByName cb_arr(0), cb_arr(1), VbMethod
End Sub


Sub YellowPaint()
Attribute YellowPaint.VB_ProcData.VB_Invoke_Func = "Y\n14"
'
' 選択セルを黄色に塗りつぶす (ctrl + shift + y)
'
    executeMacro "paint_yellow"
End Sub

executeMacro()内で、上で説明したstartMacro, endMacroを呼び出し、間にexecuteCallback()を入れています。例として選択セルを黄色く塗りつぶすYellowPaint()を書いていますが、この中ではCallback.cls内のpaint_yellow()メソッドをCallByNameによって呼び出しています。

まとめ

デザインパターンの一つであるテンプレートメソッドを使って、VBAのマクロ処理を記述しました。これが実行速度向上に寄与するかは微妙なところですが… 重たい処理を書く際は上記の前処理を入れておくべきですが、そういった処理はショートカット登録して何度も呼び出すといったことがないため効果は限定的です。まあお勉強ということで。

参考

http://cflat-inc.hatenablog.com/entry/2014/10/20/074856