エクセルVBA オブジェクト指向備忘録 依存性の注入 コーディングのヒント・カナメ 追記

20241231エクセルVBA オブジェクト指向備忘録 依存性の注入 コーディングのヒント・カナメ 追記

20241231 追記1(前回の少し解説メモ)
下記の標準モジュールとAggregatorの書き方がキモだと思う。
自分だったら、親から子を呼んでしまうけど、これは、標準モジュールの中で子を作り、親に外部注入している。

 

標準モジュール
Option Explicit

Sub Test_Aggregator()

    Dim mockGenerator As MockDataGenerator
    Dim realGenerator As DataGenerator
    Dim aggregator As Aggregator
    Dim result As Integer

    ' MockDataGeneratorでテスト
    Set mockGenerator = New MockDataGenerator   ’子
    Set aggregator = New Aggregator        ’親
    aggregator.Init mockGenerator         ’親の中で子を呼ばない(子を作らない)で、外部注入

    result = aggregator.Sum
    Debug.Print "Using MockDataGenerator: Expected: 6, Actual: " & result
    If result = 6 Then
        Debug.Print "Test Passed"
    Else
        Debug.Print "Test Failed"
    End If

    ' DataGeneratorでテスト
    Set realGenerator = New DataGenerator
    Set aggregator = New Aggregator
    aggregator.Init realGenerator

    result = aggregator.Sum
    Debug.Print "Using DataGenerator: Result: " & result
End Sub


Aggregator

Option Explicit

Private DataGenerator As IDataGenerator

' 初期化メソッドでIDataGeneratorを外部から注入
Public Sub Init(generator As IDataGenerator)
    Set DataGenerator = generator
End Sub

' 合計値を計算するメソッド
Public Function Sum() As Integer
    Dim data As Variant
    Dim total As Integer
    Dim i As Integer
    
    data = DataGenerator.Generate()
    total = 0
    
    For i = LBound(data) To UBound(data)
        total = total + data(i)
    Next i
    
    Sum = total
End Function

 

20241230 追記2
VBAでない他の言語だと、「DIコンテナ」というやり方もあるそうです。
chatGPT4o様に書いてもらいました。

VBAでは完全にはできないようです。

作ってくれたコードは、標準モジュールで、
Dim container As DIContainer
というクラスを作って、
そのクラスに、責務を任せる感じです。

 

'DIContainer

Option Explicit

Private Registry As Collection

' 初期化
Private Sub Class_Initialize()
    Set Registry = New Collection
End Sub

' クラスを登録するメソッド
Public Sub Register(className As String, instance As Object)
    On Error Resume Next
    Registry.Add instance, className
    On Error GoTo 0
End Sub

' 登録されたクラスのインスタンスを取得するメソッド
Public Function Resolve(className As String) As Object
    On Error Resume Next
    Set Resolve = Registry(className)
    On Error GoTo 0
End Function


'Aggregator

Option Explicit

Private DataGenerator As IDataGenerator

' 初期化メソッドでIDataGeneratorを外部から注入
Public Sub Init(generator As IDataGenerator)
    Set DataGenerator = generator
End Sub

' 合計値を計算するメソッド
Public Function Sum() As Integer
    Dim data As Variant
    Dim total As Integer
    Dim i As Integer
    
    data = DataGenerator.Generate()
    total = 0
    
    For i = LBound(data) To UBound(data)
        total = total + data(i)
    Next i
    
    Sum = total
End Function


IDataGenerator、DataGenerator、MockDataGeneratorクラス
これらは前述の実装と同じです。

 

テスト用モジュール (標準モジュール)
Option Explicit

Sub Test_DIContainer()

    Dim container As DIContainer
    Dim mockGenerator As MockDataGenerator
    Dim realGenerator As DataGenerator
    Dim aggregator As Aggregator
    Dim result As Integer

    ' DIコンテナを初期化
    Set container = New DIContainer
    
    ' クラスを登録
    Set mockGenerator = New MockDataGenerator
    Set realGenerator = New DataGenerator
    container.Register "MockDataGenerator", mockGenerator
    container.Register "DataGenerator", realGenerator

    ' MockDataGeneratorを使用
    Set aggregator = New Aggregator
    aggregator.Init container.Resolve("MockDataGenerator")
    result = aggregator.Sum
    Debug.Print "Using MockDataGenerator: Expected: 6, Actual: " & result
    If result = 6 Then
        Debug.Print "Test Passed"
    Else
        Debug.Print "Test Failed"
    End If

    ' DataGeneratorを使用
    Set aggregator = New Aggregator
    aggregator.Init container.Resolve("DataGenerator")
    result = aggregator.Sum
    Debug.Print "Using DataGenerator: Result: " & result

End Sub