【ExcelVBA】VBAはクラス指向より手続き型でFATに作りたい

前置き

VBAでもクラスは作成できる。

qiita.com

しかし、フォルダや階層による小分けができない。 例えば設定ファイルの保持やパースのために作ったConfigクラス、特定ファイルの書き出しのために作ったExternalXXXCSVFileクラスやExternalYYYCSVFile、内部のファイルの保持のために作ったTempFileクラス、その他値の保持用だったりビジネスロジックがぐちゃぐちゃに入り組んだりする実態が同じフォルダに入る。 そこそこの規模のアプリを作ろうとしたとき、違う考え方の領域が10個ぐらい、系3,40個のクラスが並列に並ぶことになる。これはWeb系の言語でやっているようなスケーラブル(脳死で拡張可能)なオブジェクト指向なクラスの管理だろうか?

プロジェクト(=Excelブックやxlamファイル)で分ければできるが、全部読み込むようにするのは手間だし分割も面倒、本末転倒だ。

そこそこ小さい実装ならまあいい感じの実装にはなるだろう、しかし個人としては、VBAでクラス指向*1 で設計するのはアンチパターンと位置付けるに至った。

MVCは、1つの標準モジュール/Excelシート/シートVBAを1つの「M」「V」「C」に見立ててみる

Web系に馴染みのある方であればMVCパターンを適用したくなるだろう。 ここで、それぞれの要素をModel(処理の本体)、View(ユーザーインタフェース)、Contoller(MとVの繋ぎ)で考える。(※Web系実装のMVCは通常、Mはステートフル=DBやデータなどの状態を持ち更新処理などがあるが、ここでは基本的にステートレス=データ保持無しのため、実行時パラメータによって完全に決まるものとする。)

V。Excelシートそのもの。 C。Excelシートをクリックしたときに実行するハンドラ。色々な変換処理がある。Mを呼び出す。 M。Cの結果を受けて、何かしら外部ファイルを出力したり作用したりするメソッドのみの存在。単独テストが可能。

V → Cへの繋ぎは、ExcelのRangeやCellといった各種APIで代用となる。ここら辺が特にツライが、データ化すればこっちのもの。FormやDto、Entity、Recordといった実体が欲しいのであればCreateObject("Dictionary.Scripting")で連想配列を使ったり、そこだけクラスモジュール化してもよい。 C → Mへの繋ぎは、V→Cでデータ化したものをそのまま受け渡すだけでよい。

以上、やりたいことは、引数と戻り値だけの標準モジュールで実装できないだろうか。

モジュールとImmutableを基本に設計・実装する

ローカル変数以外に、メモリ中に状態を持たず、処理結果や確保したリソースを引き続き保持したければすべて引数・戻り値を介して取得・保持し、メンバー変数やグローバル変数は必要時以外排除すると、自然とImmutableな実装は達成される。 細かい項目の受け渡しが面倒であれば、そこだけクラス化や連想配列化によりまとめて処理してしまえばよい。ここまで徹底的にやれば、晴れてクラス実装を手放せる。

代わりに、1つのモジュールの責務が肥大化するが、Excel VBAはそもそもスコープが小さくなる傾向にあるので、よほどの場合を除きコントロール可能な範囲に収まるはず。(そもそも、VBAがコントロール不能なぐらい肥大化している場合、もはやExcel VBA以外の実装を用いたほうがメリットが大きいというような事態に陥ってないだろうか?)

*1:「クラス指向」の言い回しは、オブジェクト指向に造詣の深い方にとって違和感の塊だろう、しかしあえて揶揄している。多態を持たず、静的なプログラム構造によって実際の動作および俯瞰図が完成するのであれば、メモリ上に実態を展開し実行時情報を持つというオブジェクト指向の原典的な利点の1つは別になくても、クラスという書式でカプセル化、レイヤ化、関心や責任の分離は実現できる。そしてその3点はC言語のような純手続き型言語でも設計によって十分可能であり、現実に、1970年代に軍事系のC言語の開発プロジェクトで達成されたという研究もある。クラス名のパターンが設計のすべてなのであれば、関数名+引数にとる構造体のグループでほぼ誤差なく手続き型に落とせる。