VBAでオブジェクト指向:CallByNameとポリモーフィズムで分岐なしで処理をわける方法
TwitterのExcel先輩諸氏のツイートを眺めていたら、次のような面白そうなツイートがあった。
一般的にはTypeNameなどで分岐すればよいのですが、完全に遊びでCallByNameとポリモーフィズムで分岐しないでツイートの内容を実現してみました。
クラスの普及のために書いてますが、こんなテクニックはほとんど使わないと思います。
あくまで遊びということで。
クラス定義
処理クラスのインターフェースを定義
まずは処理クラスとなるCommandを定義します。
Option Explicit
Public Sub Execute()
End Sub
インターフェースですので空実装です。
これをそれぞれのImplmentsした実装クラスを書きます。
二次元配列を処理するクラスを定義
まずは二次元配列用です。
Option Explicit
Implements Command
Private mArray As Variant
Public Sub Init(pArray As Variant)
mArray = pArray
End Sub
Private Sub Command_Execute()
'mArrayを使って何らかの処理をする
Debug.Print "Array!"
End Sub
Rangeを処理するクラスを定義
次にRange用です。
Option Explicit
Implements Command
Private mRange As Range
Public Sub Init(pRange As Range)
Set mRange = pRange
End Sub
Private Sub Command_Execute()
'mRangeを使った処理
Debug.Print "Range!"
End Sub
それぞれのクラスを生成するファクトリークラスを定義
CallByNameで実行されるメソッドを定義します。
ここにそれぞれのクラスを生成して返します。
Option Explicit
Public Function CreateCommandForVariant(pArray As Variant) As Command
Dim vCommand As ArrayCommand
Set vCommand = New ArrayCommand
Call vCommand.Init(pArray)
Set CreateCommandForVariant = vCommand
End Function
Public Function CreateCommandForRange(pRange As Range) As Command
Dim vCommand As RangeCommand
Set vCommand = New RangeCommand
Call vCommand.Init(pRange)
Set CreateCommandForRange = vCommand
End Function
ポイントはForXXXのXXXが型名であることです。
標準モジュールの定義
標準モジュールは1つです。テスト用のPrivateプロシージャがありますので、ここから実行できます。なるべくツイートにあったファンクションや実装は残しています。
Option Explicit
Function Func(a_Items As Variant)
Dim vCommand As Command
Set vCommand = ReflectionCommandFactory(a_Items)
Call vCommand.Execute
End Function
Private Function ReflectionCommandFactory(pItems As Variant) As Command
Dim vCommand As Command
Set vCommand = CallByName(New CommandFactory, ConvertMethodName(pItems), VbMethod, pItems)
Set ReflectionCommandFactory = vCommand
End Function
Private Function ConvertMethodName(pItems As Variant) As String
ConvertMethodName = "CreateCommandFor" & Replace(Replace(TypeName(pItems), "(", ""), ")", "")
End Function
Private Sub TestArray()
Dim vArray(1 To 2, 1 To 32) As Variant
Call Func(vArray)
End Sub
Private Sub TestRange()
Dim vRange As Range
Set vRange = ActiveWorkbook.ActiveSheet.Range(Cells(1, 1), Cells(10, 1))
Call Func(vRange)
End Sub
キモとなるのは、ConvertMethodName()ファンクションです。
ここで、CommandFactoryのメソッド名+型名を作っています。
二次元配列の型名を取得するとVariant()とカッコが付いてくるので、これは除去しています。
本当にVariantが来たときはちょっと考えないといけないですが、そこは目をつぶってください。
あとはポリモーフィズムで実装クラスのことは標準モジュールは知ることなく、それぞれの処理を実行できます。
これのよいところ
分岐がないのでテストがしやすいです。
また、クラス単体でテストコードが書けますので、ロジックに集中できます。
コードもシンプルです。クラスに慣れていないとどうやって実行されるのか分からないかもしれません。
気になる方は実際に張り付けて実行してみてください。
Discussion