Template MethodパターンによるVBAマクロの実装
以下の本を読み、テンプレートメソッド(Template Method)パターンというデザインパターンを学びました。
モダンC言語プログラミング 統合開発環境、デザインパターン、エクストリーム・プログラミング、テスト駆動開発、リファクタリング、継続的インテグレーションの活用
- 作者:花井 志生
- 発売日: 2019/01/31
- メディア: 単行本
せっかくなので、普段から使っているVBAのマクロをテンプレートメソッドで書いてみました。
テンプレートメソッド
テンプレートメソッドでは、(共通の前処理)→実処理→(共通の後処理) といった前後に定型処理が実施されるロジックの共通処理部分を切り出し、メソッド化するというものです。簡単なもので言うと、ファイルopen, closeやメモリ確保などリソース管理があります。
例としてファイルのopen, close処理を図で示すと以下のような構造になります。
open, closeはファイルを操作する上で必要な処理であり、中身の処理は何でもよいです。テンプレートメソッドの最も簡易な形式としては、この中身の処理をコール側で指定してやることで実現できそうです。実際の開発では共通リソースを使用するインスタンス生成などに使用できそうです。詳しくは以下のサイトが参考になるかと思います。
多くの場合、オブジェクト指向言語でデザインパターンが使用されるかと思いますが、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()
で外から呼び出す方法を取ります。詳しくは以下を参照。
実装した内容は以下です。
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のマクロ処理を記述しました。これが実行速度向上に寄与するかは微妙なところですが… 重たい処理を書く際は上記の前処理を入れておくべきですが、そういった処理はショートカット登録して何度も呼び出すといったことがないため効果は限定的です。まあお勉強ということで。