前項でAppDecoratorの共通部と固有部の分離は終わりました。ここでは、より汎用に使えるように、複数のブックと複数のBookDecoratorが使えるようにAppDecoratorを修正していきます。
マクロを分離する目的は、複数のブックに同じ機能を提供することです。したがって、同時に複数のブックを開いたときに、必要な機能を全てのブックに提供できなければなりません。しかし、前項のAppDecoratorでは、BookDecorator型の変数bookDecoのみでBookDecoratorを保持しています。1つのBookDecoratorには1つのブックが対応しますから、複数のブックでは同じ機能を使えません。
また、中規模のシステムになると、ある機能を実現するために別々のマクロによって動く複数のブックを組み合わせて使うことがあります。もし、現在作成しているフレームワークで複数のBookDecoratorを保持できるようになれば、複数のシートにまたがっているマクロを一元的に管理することが出来るでしょう。
では、以上2つの機能を現在まで作成してきたフレームワークに組み込むように修正してみましょう。そのためには、
を扱えることが必要になります。複数のBookDecoratorを扱うには、BookDecoratorの代わりにBookDecoratorのコレクションを使うだけです。では、多様なBookDecoratorを扱うにはどうすればよいでしょうか。
比較のために、前項で作成したBookDecoratorの初期化ルーチンをもう一度見てみましょう。
[AppDecorator]
Private Sub CreateBookDecorator(wb As Workbook)
If bookCheck.CheckBook(wb) = True Then
Set bookDeco = New BookDecorator
Set bookDele = New ConcreteBookDelegate
bookDeco.Initialize wb, bookDele, Me
End If
End Sub
[BookChecker]
Public Function CheckBook(wb As Workbook) As Boolean
'ブックの条件式をここに書く
End Function
Public Function CreateBookDelegate() As Object
Set CreateBookDelegate = New ConcreteBookDelegate
End Function
|
AppDecoratorは、各ブックに固有の処理を担当するBookDelegate(の疑似派生クラス)の作成処理をBookCheckerに委譲しています。ですから、別のBookDelegateをもつBookDecoratorを作成するには、別のBookDelegateを生成するためのBookCheckerの疑似派生クラスを作成すればよいことになります。従って、複数種なBookDecoratorを扱うには、複数のBookCheckerを使えるようにする必要があります。
以上の前提をふまえて実装していきましょう。まずはメンバ変数の宣言です。
Public WithEvents app As Application Private bookDecorators As New Collection 'of BookDecorator Private bookCheckers As New Collection 'of BookChecker |
BookDecoratorとBookCheckerがそれぞれコレクションとして管理されています。
Public Sub Initialize()
Set app = Application
Dim wb As Workbook
SetBookCheckers
If bookCheckers.Count > 0 Then
For Each wb In Workbooks
CreateBookDecorator wb
Next
Else
Unload Me
End If
End Sub
Private Sub SetBookCheckers()
End Sub
|
初期化ルーチンです。ここで注目していただきたいのは、Initializeメソッドの中ほどから呼び出されているSetBookCheckersです。この追加されたメソッドは、BookCheckerのコレクションへの登録をおこなうためのものです。SetBookCheckersメソッドは、必要なBookDecoratorを疑似継承したクラスをbookCheckersコレクションに登録します。実装はマクロごとに異なりますから、デフォルトの処理はなく、状況に応じて加筆します。あるBookDecoratorであるConcreteBookChecker1, 2を登録するには以下のようにします:
Private Sub SetBookCheckers()
bookCheckers.Add Item:=New ConcreteBookChecker1
bookCheckers.Add Item:=New ConcreteBookChecker2
End Sub
|
AppDecoratorのSetBookCheckersメソッドは各AppDecoratorに固有の処理です。これまでの議論から言えば、この処理は新たなクラスAppDelegateを作成して委譲すべきと感じるかもしれません。そこで、SetBookCheckersメソッドを分離しなかった理由について補足します。
BookDecoratorなどでは、BookDelegateに固有の処理を分離・委譲していました。このようにしたのは、複数種のBookDecoratorを1つのマクロで使用する際に、書き換えなければいけない箇所を減らすためでした。一方、異なるExcelのアプリケーションを使うことはないですから、AppDecoratorは複数になりません。したがって、AppDecoratorの場合は、それ自身の中に変更すべき箇所を書いた方が、加筆修正の量が少なくてすみます。そこでAppDecoratorだけはDelegateを持たないように設計してあります。
また、BookCheckerは、BookDecoratorの生成の時だけに関与するクラスであり、Excelアプリよりむしろブックに密接しているクラスです。そこでこのクラスにもAppDelegateという名称を与えていません(BookFactoryの良かったかも・・・)。
では、先を見てみましょう。CreateBookDecoratorメソッドの修正例です:
Private Sub CreateBookDecorator(wb As Workbook)
Dim bc As Object 'BookChecker
For Each bc In bookCheckers
If bc.CheckBook(wb) = True Then
Dim bd As New BookDecorator
bd.Initialize wb, bc.CreateBookDelegate, Me
bookDecorators.Add Item:=bd
End If
Next
End Sub
|
各BookCheckerについて走査し、条件が合えばBookCheckerが指定するBookDelegateと結合されたBookDecoratorがbookDecoratorsコレクションに格納されます。
次に、閉じるときの処理です:
Private Sub app_WorkbookBeforeClose(ByVal wb As Workbook, Cancel As Boolean)
Dim bd As BookDecorator
Dim i As Integer
i = 1
For Each bd In bookDecorators
If bd.wbook Is wb Then
bookDecorators.Remove i
End If
i = i + 1
Next
End Sub
|
格納されているBookDecoratorを走査し、閉じようとしているブックのBookDecoratorを全て解放しています。
以上のような変更によって、複数のブックと複数のBookDecoratorが使えるようになり、フレームワークの再利用性があがりました。