4.2 Decoratorの機能の委譲

前項で書いたように、目的は機能を共通部と固有部に分割することにより、再利用可能なフレームワークを作成することです。そこで、Decoratorの機能の分離するにはどうしたらよいのか考えてみましょう。

BookDecoratorの機能には、以下の2つに分けられます:

このうち、ブックのイベントをフックする部分は共通ですが、イベントの中身自体はそれぞれのブックで固有です。また、シートのDecoratorは必ず設定しますが、どのようなDecoratorを設定するかは異なるかもしれません。これらの前提に基づいて、共通部分をBookDecorator、固有部分を別クラスのBookDelegateに分離して実装すれば、所期の目的が達成されることになります。

では、いつもの如しでBookDecoratorとBookDelegateの実装全体を示して、それから各所を説明していきます。


[BookDecorator]
Public WithEvents wbook As Workbook
Public parent As AppDecorator
Private bookDele As Object
Private sheetDecorators As New Collection 'of SheetDecorator

Public Sub Initialize(wb As Workbook, d As Object, ad As AppDecorator)
    Set wbook = wb
    Set parent = ad
    Set bookDele = d
    If Not bookDele Is Nothing Then
        bookDele.Initialize Me
        bookDele.SetSheetDecorators
    End If
    Dim sd As SheetDecorator
End Sub

Public Sub AddSheetDecorator(sd As SheetDecorator)
    sheetDecorators.Add Item:=sd
End Sub

[BookDelegate]
Public WithEvents wbook As Workbook
Public parent As BookDecorator

Public Sub Initialize(bd As BookDecorator)
    Set wbook = bd.wbook
    Set parent = bd
End Sub

Public Sub SetSheetDecorators()

End Sub

それでは部分ごとに見ていきます。まずはBookDecoratorの実装です。


Public WithEvents wbook As Workbook
Public parent As AppDecorator
Private bookDele As Object
Private sheetDecorators As New Collection 'of SheetDecorator

メンバ変数は前章で扱ったBookDecoratorに、移譲先のクラスを保持するbookDeleを追加してあります。委譲を実現するため、2章のようにObject型で受けています。


Public Sub Initialize(wb As Workbook, d As Object, ad As AppDecorator)
    Set wbook = wb
    Set parent = ad
    Set bookDele = d
    If Not bookDele Is Nothing Then
        bookDele.Initialize Me
        bookDele.SetSheetDecorators
    End If
    Dim sd As SheetDecorator
End Sub

初期化メソッドです。前章と違うのは、If Not bookDele〜の部分ですね。まず自分自身を引数にしてBookDelegate.Initializeを呼び出し、BookDecoratorとBookDelegateを関係づけます。そのあと、委譲されたSheetDecoratorを設定するメソッド(BookDelegate.SetSheetDecorators)を呼び出しています。


Public Sub AddSheetDecorator(sd As SheetDecorator)
    sheetDecorators.Add Item:=sd
End Sub

SheetDecoratorの保持自体はBookDecoratorが行いますが、SheetDecoratorの取得はBookDelegateに委譲されています。したがって、BookDelegateは、自信が作成したSheetDecoratorを、BookDecoratorのsheetDecoratorsコレクションに放り込まなければなりません。そこで、AddSheetDecoratorメソッドを呼び出すことで、明示的にsheetDecoratorsコレクションを操作するようにしてあります。

では、移譲先のBookDelegateです。


Public WithEvents wbook As Workbook
Public parent As BookDecorator

BookDelegateでは、各マクロに固有なイベントを扱います。そのために、BookDecoratorと同様、Workbookのイベントをフックする変数wbookを持ちます。


Public Sub Initialize(bd As BookDecorator)
    Set wbook = bd.wbook
    Set parent = bd
End Sub

Public Sub SetSheetDecorators()

End Sub

初期化部分は、フック先のブックと親に当たるBookDecoratorとの関係付けですので見たままかと思います。そして、SetSheetDecoratorsメソッドに、SheetDecoratorを設定する処理を書くことになります。メソッドの中身は使用するブックにあうように書きます。

では、SetSheetDecoratorsメソッドの例を考えます。SheetDecoratorも、BookDecoratorと同様にSheetDelegateに独自部分を分離することができます。そうすると、SheetDecoratorとSheetDelegateを擬似継承したクラスを組み合わせる処理が必要です。そこで、ワークシート1のSheetDecoratorを、ある移譲先クラスConcreteSheetDelegateを関係づけて初期化するメソッドはこんな風になります:


Public Sub SetSheetDecorators()
    parent.AddSheetDecorator CreateConcreteSheetDecorator(wbook.Worksheets(1))
End Sub

Public Function CreateConcreteSheetDecorator(ws As Worksheet) As SheetDecorator
    Dim sdeco As New SheetDecorator
    Dim sdele As New ConcreteSheetDelegate
    sdeco.Initialize ws, sdele, parent
    Set CreateConcreteSheetDecorator = sdeco
End Function

CreateConcreteSheetDecoratorは、ある組み合わせを作成するFactory Methodにあたります。SetSheetDecoratorsメソッドは、このメソッドによって確保されたSheetDecoratorをBookDecoratorに渡しているだけです。

そうそう、SheetDecoratorの実装を示さないと、「sdeco.Initialize〜」の部分がチンプンカンプンですね。SheetDecoratorとSheetDelegateの実装を示しておきます。話は子に当たるDecoratorがない分BookDecoratorより単純です。


[SheetDecorator]
Public WithEvents wsheet As Worksheet
Public parent As BookDecorator
Private sheetDele As Object

Public Sub Initialize(ws As Worksheet, d As Object, bd As BookDecorator)
    Set wsheet = ws
    Set parent = bd
    Set sheetDele = d
    If Not sheetDele Is Nothing Then
        sheetDele.Initialize Me
    End If
End Sub

[SheetDelegate]
Public WithEvents wsheet As Worksheet
Public parent As SheetDecorator

Public Sub Initialize(sd As SheetDecorator)
    Set wsheet = sd.wsheet
    Set parent = sd
End Sub