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
というクラスを作って、
そのクラスに、責務を任せる感じです。
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