VBAでインターフェイス

VBAでインターフェイス
---
自分はVBA歴が長い方だと思いますが、VBAでインターフェイスが使えることを最近知りました。それはもうビックリして、少し感動しました。
とは言え今すぐにこれを使って何かを作るなんてことはありそうもなく、とりあえず備忘録として、試したことをまとめておきたいと思います。

インターフェイスとは
---
インターフェイスとは、クラスや構造体が定義するプロパティやメソッドなどのシグネチャ（メソッドの引数や戻り値の型、メソッドの名前など）のみを書いたものです。プロパティやメソッドの識別のみが目的で実装は一切ありません。インターフェイスを実装するクラスはそのインターフェイスのすべてのメンバを実装しなければなりません。

クラスモジュールで作る
---
まずはインターフェイス用のクラスモジュールを追加します。名前は`IAnimal`としました。
とりあえず一つ、メソッドのシグネチャを以下のように書きます。アクセス修飾子はリファレンスでは`Public`を書くと説明されていますが、必要ないでしょう。（VB.NETやC#に合わせて）

```vb:IAnimal.cls

Sub CallOut()
    
End Sub

```

簡単なんで、ついでにもう一つ別のインターフェイスも書いておきましょう。名前は`IDebug`として、ここにもメソッドを一つ書きます。

```vb:IDebug.cls

Sub DebugPrint()
    
End Sub

```
次に、これら二つのインターフェイスを実装するための`Cat`クラスを作ります。`Cat`クラス用のクラスモジュールを追加して、宣言セクションに`Implements`キーワードに続けてインターフェイス名を書きます。
```vb:Cat.cls
Implements IAnimal
Implements IDebug
```
すると、オブジェクトボックスに２つのインターフェイスが現れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294209/cb387043-6185-e9b8-0ca3-086abecabd20.png)

`IAnimal`インターフェイスを選択すると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294209/32fa8c29-b7e8-afc2-a0f8-1a0768eeaba3.png)

`IAnimal_CallOut`メソッドが出現します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294209/428f5395-8c01-452f-cc6d-593ade9457ce.png)

* 全てのメンバが出現するわけではない（インターフェイスのクラスモジュールで一番上に書かれたものだけ）
* 全てのメンバを実装しないとエラーとなる（インターフェイスのメンバが複数の場合）
* 手書きで実装する場合、インターフェイス名とアンダースコアをメソッド名（またはプロパティ名）に付加する
* 出現したメンバのアクセス修飾子は`Private`がつく
* インターフェイス側にはアクセス修飾子をつけない（リファレンスでは`public`を付けるとの説明があるが不要）
* 出現したメソッドを`Public`にすればインスタンスで使用できるようになる
* その場合、利用時のメソッド名は`CallOut`ではなく`IAnimal_CallOut`となる


以下に`Cat`クラスの簡単な実装を書いてみました。

```vb:Cat.cls

' 実装するインターフェイスを設定する（複数のインターフェイスを設定可能）
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`クラスも書いてみます。

```vb:Dog.cls

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

```



２つのクラスを使ってみる
---
準備が整ったのでモジュールを追加してインターフェイスを実装した`Cat`クラスと`Dog`クラスを使ってみます。

```vb:Module1.bas

Sub x()

    ' ２つのクラスとコレクションの生成
    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`メソッドを書き、ループ内で実行させてみたテストの痕跡です。
これを動作させるとどうなるか:question:列挙されるアイテムを受ける`hg`変数は`IAnimal`型ですが、これはエラーとなり実行できません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294209/1ceab4ab-fcfc-8ae8-6aa7-91912fcf9c25.png)

続く二つのループでは`IAnimal`インターフェイスのメンバを実装した`CallOut`メソッドと、`IDebug`インターフェイスのメンバを実装した`DebugPrint`メソッドを実行させています。
そして、これは問題なく実行されます:bangbang:
コレクション内の`neko`と`inu`は別々のクラスのインスタンスですが、同じ構文でメソッドを実行できています。正にここがキモ、ポリモーフィズムってやつですね:laughing:

`Cat`クラスの`CallOut`では`Debug.Print`で「にゃー」と鳴かせていますが、`Dog`クラスの`CallOut`では`Debug.Print`ではなく`MsgBox`で鳴かせています。この様に同じ`CallOut`でも全く違った挙動をさせることができます。

この他にもインターフェイスの効用として**疎結合**（クラスの変更容易性）やクラスの特性・動作をユーザーに約束する**機能保証**などがあります。

以下、インターフェイスについての良い説明記事がありました。

https://qiita.com/yutorisan/items/d28386f168f2f3ab166d

残念ながら`IAnimal`型のインスタンスで`Cat`クラス（及び`Dog`クラス）の`Hoge`メソッドは呼び出せません。これはVB.NETであればキャストして呼び出せます。

```vb:Program.vb

Dim pet As IAnimal = New Cat

DirectCast(pet, Cat).Hoge()

```

さいごに独り言
---
今年は業務ツールを作る際、意識的にVB.NETを避けてC#で書いていましたが、この記事の検証のために久々にVB.NETに触れ、何とも懐かしくて胸躍りました。

やっぱりVBはええなあ、、、

:santa_tone1::christmas_tree::santa_tone1::christmas_tree::santa_tone1::christmas_tree:

https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/implements-statement

追記（2024.12.13）
--
上述のコメントアウトした`Hoge`メソッドですが、`Object`で受けるとエラーになりませんでした:sweat_smile:

```vb
Dim hg As Object
For Each hg In AnimalBox
     hg.Hoge
Next
```