今回は公開されている既存のライブラリ「Ariawase」を使って解説したいと思う。
Ariawaseは次のページから入手できる。
ページを開くと右下のほうにZipでダウンロードできるボタンがあるのでこちらを入手する。
zipを解凍するとbuild.batファイルがあるのでそれを使ってビルドするのだが、あらかじめExcelでマクロのセキュリティを次のように設定しておく必要がある。
そしてbuild.batを実行すると、binフォルダが作成され、その中に「Ariawase.xlsm」が入っている。
Ariawaseは便利なライブラリなのだが、以下を見てのとおり、モジュール数が多いのが難点である。
そもそも自由にモジュールを入れるフォルダを作れたら何も問題はないのだが、VBAでは残念なことに標準モジュール・クラスモジュールなどのデフォルトフォルダの配下をさらに整理することはできない。
そこで今回はこのAriawaseをアドイン化してメインマクロの入ったブックとは分離しようという試みである。
アドイン化自体はすこぶる簡単で、名前を付けて保存する際にファイルの種類でアドインを選ぶだけだ。
そしてExcelのファイルタブ→オプション→アドイン→設定とたどり、追加したアドインを有効にする。
次に別ファイルからこのアドインを参照設定する必要があるが、プロジェクト名がVBAProjectのままでは後でどれがAriawaseのアドインかわからなくなるのでプロパティからプロジェクト名をAriawaseに変えておく。
そしてVBEでBook1に戻ってツールメニューから参照設定を行う。
すると、プロジェクトエクスプローラーに次のように参照設定が表示される。
さて、これでたとえばAriawaseのArrayExクラスを使ってみようと思う。
Book1に標準モジュールを追加し、次のようなコードを記入する。
Sub test() Dim AE As ArrayEx End Sub
しかし実行してみると…
エラーがでる。
クラスモジュールは通常Privateに設定されているため、他のブックから直接インスタンス化するすることはできない。
しかし設定できるのはPrivateかPublicNotCreatableのみ。
NotCreatableを選択すると、Newキーワードが使えないので結局インスタンス化できない。
しかしこれを回避する方法はある。
他のブックからインスタンスが作れないなら、Ariawaseアドイン内でインスタンスを生成して返す関数を用意すればよい。
Ariawaseアドイン側に標準モジュールを挿入し、次のコードを張り付ける。
Public Function NewArrayEx() As Object Set NewArrayEx = New ArrayEx End Function
そしてBook1側では、次のように呼び出す。
Sub test() Dim AE As Object Set AE = Ariawase.NewArrayEx End Sub
こうすると、ArrayEx自体がプライベートクラスであってもインスタンスを得ることができる。
しかしこのままではコーディング中にプロパティなどのヒントが出ないので使い勝手が悪い。
これは、インターフェースを使うことで回避できる。
まずAriawase側に新しいクラスモジュールを追加し、IArrayExとしておく。
IArrayExには次のコードを書いて、プロパティウインドウでPublicNotCreatableにしておく。
Option Explicit Public Function AddVal(ByVal val As Variant) End Function Public Function AddObj(ByVal obj As Variant) End Function Public Function ToArray() As Variant End Function
そして、元のArrayExクラスのOption Explictの下あたりに「Implements IArrayEx」と追記し、さらにPublic Funcitonの名前の先頭に、IArrayEx_を付与する。
改造後のコードはこちら。
(著作権は、いげ太さんが保有します。再掲される場合はMITライセンスにしたがってください。)
'Copyright (c) 2011-2015 igeta '''+---- --+ '''| Ariawase 0.6.0 | '''| Ariawase is free library for VBA cowboys. | '''| The Project Page: https://github.com/vbaidiot/Ariawase | '''+-- ----+ Option Explicit Implements IArrayEx Private xItems As Variant Private xLength As Long Private xIndex As Long Private Sub Class_Initialize() xIndex = -1 xLength = -1 + 32 ReDim xItems(xLength - 1) End Sub Private Sub Extend() If xIndex < xLength Then GoTo Escape xLength = xLength + 1 'possible overflow (Err.Raise 6) xLength = -1 + xLength + xLength ReDim Preserve xItems(xLength - 1) Escape: End Sub Public Function IArrayEx_AddVal(ByVal val As Variant) xIndex = xIndex + 1 Extend Let xItems(xIndex) = val End Function Public Function IArrayEx_AddObj(ByVal obj As Variant) xIndex = xIndex + 1 Extend Set xItems(xIndex) = obj End Function Public Function IArrayEx_ToArray() As Variant Dim arr As Variant: arr = xItems If xIndex > -1 Then ReDim Preserve arr(xIndex) Else arr = Array() End If IArrayEx_ToArray = arr End Function
さて、これでアドイン側の準備はできた。
そしてBook1で次のコードを入力してみよう。
Sub test() Dim AE As IArrayEx Set AE = Ariawase.NewArrayEx AE.AddVal 1 AE.AddVal 3 AE.AddVal 5 AE.AddVal 7 AE.AddVal 9 For Each x In AE.ToArray Debug.Print x Next End Sub
すると、入力途中でプロパティなどの候補が表示されることに気付いたかもしれない。
この場合のインターフェースは「取り決め・規約」といった意味を持つ。
インターフェースがなければ、Book1はAriawaseで生成されたブジェクトを汎用オブジェクト型として受け取ることになる。
その結果、どのようなメソッドやプロパティがあるのかVBコンパイラでは判断できない。
つまり、インターフェースが無ければ、コードの正しさは、プログラマーが保証しなければならない。
もしインターフェースがあれば、コードの正しさはコンパイラが保証してくれる。
プログラムが複雑になればなるほど、インターフェースの恩恵は大きい。
ということで、最後のほうはインターフェースの紹介に終わってしまったが、まとめると、ライブラリとメインコードを分離するとスッキリする、またVBプロジェクト間のオブジェクト受け渡しでインターフェースが活躍するという話である。