VBAでインターフェイス
自分はVBA歴が長い方だと思いますが、VBAでインターフェイスが使えることを最近知りました。それはもうビックリして、少し感動しました。
とは言え今すぐにこれを使って何かを作るなんてことはありそうもなく、とりあえず備忘録として、試したことをまとめておきたいと思います。
インターフェイスとは
インターフェイスとは、クラスや構造体が定義するプロパティやメソッドなどのシグネチャ(メソッドの引数や戻り値の型、メソッドの名前など)のみを書いたものです。プロパティやメソッドの識別のみが目的で実装は一切ありません。インターフェイスを実装するクラスはそのインターフェイスのすべてのメンバを実装しなければなりません。
クラスモジュールで作る
まずはインターフェイス用のクラスモジュールを追加します。名前はIAnimal
としました。
とりあえず一つ、メソッドのシグネチャを以下のように書きます。アクセス修飾子はリファレンスではPublic
を書くと説明されていますが、必要ないでしょう。(VB.NETやC#に合わせて)
Sub CallOut()
End Sub
簡単なんで、ついでにもう一つ別のインターフェイスも書いておきましょう。名前はIDebug
として、ここにもメソッドを一つ書きます。
Sub DebugPrint()
End Sub
次に、これら二つのインターフェイスを実装するためのCat
クラスを作ります。Cat
クラス用のクラスモジュールを追加して、宣言セクションにImplements
キーワードに続けてインターフェイス名を書きます。
Implements IAnimal
Implements IDebug
すると、オブジェクトボックスに2つのインターフェイスが現れます。
IAnimal
インターフェイスを選択すると、
IAnimal_CallOut
メソッドが出現します。
- 全てのメンバが出現するわけではない(インターフェイスのクラスモジュールで一番上に書かれたものだけ)。
- 全てのメンバを実装しないとエラーとなる(インターフェイスのメンバが複数の場合)。
- 手書きで実装する場合、インターフェイス名とアンダースコアをメソッド名(またはプロパティ名)に付加する。
- 出現したメンバのアクセス修飾子は
Private
がつく。 - インターフェイス側にはアクセス修飾子をつけない(リファレンスでは
public
を付けるとの説明があるが不要)。 - 出現したメソッドを
Public
にすればインスタンスで使用できるようになる。 - その場合、利用時のメソッド名は
CallOut
ではなくIAnimal_CallOut
となる。
以下にCat
クラスの簡単な実装を書いてみました。
' 実装するインターフェイスを設定する(複数のインターフェイスを設定可能)
Implements IAnimal
Implements IDebug
Private Const meow As String = "にゃー"
Public Sub Hoge()
MsgBox "Hoge - 猫ほげ", vbInformation
End Sub
Private Sub IAnimal_CallOut()
Debug.Print "CallOut - " & meow
End Sub
Private Sub IDebug_DebugPrint()
Debug.Print "DebugPrint - わたしは猫です"
End Sub
ついでにDog
クラスも書いてみました。
Implements IAnimal
Implements IDebug
Private Const bowwow As String = "わんわん"
Public Sub Hoge()
MsgBox "Hoge - 犬ほげ", vbInformation
End Sub
Private Sub IAnimal_CallOut()
MsgBox "CallOut - " & bowwow, vbInformation
End Sub
Private Sub IDebug_DebugPrint()
Debug.Print "DebugPrint - わいは犬や"
End Sub
2つのクラスを使ってみる
準備が整ったのでモジュールを追加してインターフェイスを実装したCat
クラスとDog
クラスを使ってみます。
Sub x()
' 2つのクラスとコレクションの生成
Dim AnimalBox As New Collection
Dim neko As New Cat: AnimalBox.Add neko
Dim inu As New Dog: AnimalBox.Add inu
' Dim hg As IAnimal
' For Each hg In AnimalBox
' hg.Hoge
' Next
Dim pet As IAnimal
For Each pet In AnimalBox
pet.CallOut
Next
Dim dbg As IDebug
For Each dbg In AnimalBox
dbg.DebugPrint
Next
End Sub
まずは、二つのクラスのインスタンスをコレクションに追加します。
その下のコメントアウトしたFor Each
は、二つのクラスに敢えて同名でHoge
メソッドを書き、ループ内で実行させてみたテストの痕跡です。
これを動作させるとどうなるか列挙されるアイテムを受けるhg
変数はIAnimal
型ですが、これはエラーとなり実行できません。
続く二つのループではIAnimal
インターフェイスのメンバを実装したCallOut
メソッドと、IDebug
インターフェイスのメンバを実装したDebugPrint
メソッドを実行させています。
そして、これは問題なく実行されます
コレクション内のneko
とinu
は別々のクラスのインスタンスですが、同じ構文でメソッドを実行できています。正にここがキモ、ポリモーフィズムってやつですね
Cat
クラスのCallOut
ではDebug.Print
で「にゃー」と鳴かせていますが、Dog
クラスのCallOut
ではDebug.Print
ではなくMsgBox
で鳴かせています。この様に同じCallOut
でも全く違った挙動をさせることができます。
この他にもインターフェイスの効用として疎結合(クラスの変更容易性)やクラスの特性・動作をユーザーに約束する機能保証などがあります。
以下、インターフェイスについての良い説明記事がありました。
残念ながらIAnimal
型のインスタンスでCat
クラス(及びDog
クラス)のHoge
メソッドは呼び出せません。これはVB.NETであればキャストして呼び出せます。
Dim pet As IAnimal = New Cat
DirectCast(pet, Cat).Hoge()
さいごに独り言
今年は業務ツールを作る際、意識的にVB.NETを避けてC#で書いていましたが、この記事の検証のために久々にVB.NETに触れ、何とも懐かしくて胸躍りました。
やっぱりVBはええなあ、、、