前項ではブックをフックするBookDecoratorから固有の部分をBookDelegateとして分離させる方法を示しました。この項ではその上位に位置するAppDecoratorの実装を、共通部分を固有部分という観点から考えていきましょう。
AppDecoratorはExcelの最上位のイベントをフックするためのクラスです。AppDecoratorはBookDecoratorを保持し、その取得と解放を行います。まずはAppDecoratorの機能を検討するために、前章で扱ったfoo.xls関係のAppDecoratorを示します:
Private WithEvents app As Application
Private bookDeco As BookDecorator
Public Sub Initialize()
Set app = Application
Dim wb As Workbook
For Each wb In Workbooks
CreateBookDecorator
Next
End Sub
Private Sub CreateBookDecorator(wb As Workbook)
If wb.Name = "foo.xls" Then
Set bookDeco = New BookDecorator
bookDeco.Initialize wb
End If
End Sub
Private Sub app_WorkbookOpen(ByVal wb As Workbook)
CreateBookDecorator
End Sub
Private Sub app_WorkbookBeforeClose(ByVal wb As Workbook, Cancel As Boolean)
If wb.Name = ("foo.xls") Then
Set bookDeco = Nothing
End If
End Sub
|
では、この中で、どの動作がブック固有で、どれが共通しているかを検討してみましょう。とはいっても、ブック固有の処理は"foo.xls"が出てくる部分だけです。そこで、foo.xlsが出ている2ヶ所について考えてみます。
この2ヶ所は何をしているのでしょうか。CreateBookDecoratorでは、BookDecoratorを確保する前に、BookDecoratorを確保すべきかどうかを判定しています。そしてWorkbookBeforeCloseイベントの中では、ブックを閉じる際に、閉じようとしているブックにはBookDecoratorが設定されているのかを判定し、必要に応じてBookDecoratorを解放しています。そこで、これら処理を一般化して文字で書くと以下のようになるでしょう:
Private Sub CreateBookDecorator(wb As Workbook)
If 指定したブックがDecoratorの取得条件に合っている Then
Set bookDeco = New BookDecorator
bookDeco.Initialize wb
End If
End Sub
Private Sub app_WorkbookBeforeClose(ByVal wb As Workbook, Cancel As Boolean)
If 閉じようとしているブックがBookDecoratorを持っている Then
Set bookDeco = Nothing
End If
End Sub
|
このうち、後者、すなわちWorkbookBeforeCloseメソッド中の条件文は、汎用的な方法で書き直せます。「閉じようとしているブックがBookDecoratorを持っている」ということは、「閉じようとしているのがBookDecoratorがフックしているブックかどうか」ということです。つまり、閉じようとしているブックと、BookDecoratorが保持しているブックが同一かどうかがわかればオーケーです。従って、以下のように書けます:
Private Sub app_WorkbookBeforeClose(ByVal wb As Workbook, Cancel As Boolean)
If bookDeco.wbook Is wb Then
Set bookDeco = Nothing
End If
End Sub
|
つぎに、Initializeメソッドに今一度焦点を当てて、もう一度固有部分の分離について考えていきます。前項で扱ったように、今使っているBookDecoratorは固有の処理をBookDelegateに分離したものを想定しています。前章で扱ったとおり、AppDecoratorはBookDecoratorを作成する責任があります。したがって、BookDecoratorを作成するメソッドには、適切なBookDelegateを関連づける処理が必要になります。
そこで、BookDecoratorをある移譲先クラスConcreteBookDelegateと関連づけたいとしましょう。このとき、作成するCreateBookDecoratorメソッドは以下のようになります。
Private Sub CreateBookDecorator()
If 指定したブックがDecoratorの取得条件に合っている Then
Set bookDeco = New BookDecorator
Set bookDele = New ConcreteBookDelegate
bookDeco.Initialize wb, bookDele, Me
End If
End Sub
|
なお、前章の例ではBookDecoratorはAppDecoratorを保存していませんでしたが、一般化するために、BookDecoratorのparent変数によってAppDecoratorを保持するようにしてあります(前項参照)。そこでBookDecorator.InitializeメソッドにはAppDecoratorが引数として追加されています。最後のMeのことですね。
それでは、AppDecoratorの機能のうち、固有の処理を分離してみましょう。今回「取得条件」の他に、新たに「適切なBookDelegateを取得する」という固有の機能が追加されています。この2つの機能は互いに関連がありますので、これらを1つのクラスに分離します。このクラスはAppDelegateでも良いのですが、後で述べる理由によってBookCheckerという名前にしておきます。そこで、BookCheckerの枠を示します:
[BookChecker]
Public Function CheckBook(wb As Workbook) As Boolean
'ブックの条件式をここに書く
End Function
Public Function CreateBookDelegate() As Object
'適当なBookDelegateを返す
End Function
|
CreateBookDelegateメソッドの例として、ConcreteBookDelegateを返すメソッドを示せば、以下のようになります:
Public Function CreateBookDelegate() As Object
Set CreateBookDelegate = New ConcreteBookDelegate
End Function
|
CheckBookメソッドは、BookCheckerを擬似的に継承したクラスで書き加えます。そして、BookCheckerの中身を書き加えたあるクラスConcreteBookCheckerを使う時を例にすれば、AppDecoratorは以下のように修正されます:
[AppDecorator]
Private bookCheck As Object 'BookChecker
(略)
Private Sub Initialize()
(略)
Set bookCheck = New ConcreteBookChecker
End Sub
Private Sub CreateBookDecorator(wb As Workbook)
If bookCheck.CheckBook(wb) = True Then
Set bookDeco = New BookDecorator
Set bookDele = bookCheck.CreateBookDelegate
bookDeco.Initialize wb, bookDele, Me
End If
End Sub
|
これで、固有の処理を分離したAppDecoratorができあがりました。最後にソースを今一度示します。
[AppDecorator]
Private WithEvents app As Application
Private bookDeco As BookDecorator
Private bookCheck As Object 'BookChecker
Public Sub Initialize()
Set app = Application
Dim wb As Workbook
Set bookCheck = New ConcreteBookChecker
For Each wb In Workbooks
CreateBookDecorator
Next
End Sub
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
Private Sub app_WorkbookOpen(ByVal wb As Workbook)
CreateBookDecorator
End Sub
Private Sub app_WorkbookBeforeClose(ByVal wb As Workbook, Cancel As Boolean)
If bookDeco.wbook Is wb Then
Set bookDeco = Nothing
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
|