前回の「VBAにおけるクラスの継承(擬似)について考える」の続きです。
VBAにて、クラスの継承のような形を擬似的に作る方法について書かせていただきました。ただ、その方法では、保守性などに課題があるため、規模の大きな実装には不向きという内容でした。
今回紹介するインターフェースクラスを作るImplementsは、使い方には一癖ありますが、ポリモーフィズムを実現できるので、使いこなすことでより質の高いコードを書けるようになります。
また、これを使うことで、前回の課題解決が可能です。
「やりたいことのイメージ」は前回の記事と同じ内容なので、既に読まれた方はSKIPで構いません。
①やりたいことのイメージ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
from abc import ABC, abstractmethod # 抽象基底クラス class Job(ABC): def __init__(self, name): self.name = name @abstractmethod def attack(self): pass def defense(self): return f"{self.name}は身を守っている" # Jobを継承したナイトクラス class Knight(Job): def attack(self): return f"{self.name}は剣で攻撃した" # Jobを継承した魔法使いクラス class Mage(Job): def attack(self): return f"{self.name}は杖で攻撃した" def magic(self, spell): return f"{self.name}は{spell}の魔法を使った" # Jobを継承したヒーラークラス class Healer(Job): def attack(self): return f"{self.name}は杖で攻撃した" def heal(self, target): return f"{self.name}は{target}を回復した" # メイン処理実行 if __name__ == "__main__": # 各インスタンスを生成 knight = Knight("nao") mage = Mage("Uta") healer = Healer("re") # 行動の出力 print(knight.attack()) # ナイトの攻撃 print(knight.defense()) # ナイトの防御 print(mage.magic("ファイア")) # 魔法使いの魔法攻撃 print(healer.heal(knight.name)) # ヒーラーの回復 |
※詳しくは前の記事を参照願います
②共通の実装
CharacterStatusクラスは前回と同じであるため、記載を割愛します。
ParentClassは下記のようにAttackを削除し、Attackは子クラスでの実装のみとします。
|
1 2 3 4 5 6 7 8 9 10 11 |
Option Explicit Private status_ As New CharaterStatus Public Property Get status() As CharaterStatus Set status = status_ End Property Public Sub Defense() Debug.Print status_.name & "は身を守っている" End Sub |
③Implementsを使ってみる
Implementsというのは共通のインターフェースをクラスに持たせる機能です。言葉だけではわかりにくいので、さっそくコードを見てみましょう。下記がIJobインターフェースクラスです。
|
1 2 3 4 5 6 7 |
Option Explicit Public Property Get parent() As ParentClass End Property Public Sub Attack() End Sub |
このように、外部から呼び出すため、publicで宣言だけ書いてあげます。この後Implementsを使って作る各実装クラスで共通で使うもののみ記載します。
実装クラスについては、まずJobKnightから見ていきましょう。
・JobKnight
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Option Explicit Implements IJob Private parent_ As New ParentClass Private Property Get IJob_parent() As ParentClass Set IJob_parent = parent_ End Property Private Sub IJob_Attack() Debug.Print parent_.status.name & "は剣で攻撃した" End Sub |
上記記載のように、実装クラスにはImplements インタフェースクラス(IJob)宣言を記述します。これにより、実装クラスとしての機能が有効になります。試しに、この時点でコード画面の左上のオブジェクトリストを見てみてください。
下記のように、IJobが選択できるようになっているはずです。

なお、実装クラスでは、インターフェースクラスで宣言したプロパティやメソッドを全て実装する必要があります。
その際、メソッド名などの書き方はインターフェースクラス名_メソッド名のような書き方をする必要があります。(書かないとエラーになります)
手入力するのはやや面倒なのとミスタイプもあり得るので、上記のようにIJobオブジェクトを選択して、右のプロシージャリストから選択するのがおすすめです。
補足ですが、Implementsを使う場合は、これらの共通処理は隠蔽しておきたいので、Privateで定義します。
続いて、JobMageとJobHealerを見ていきます。
・JobMage 独自メソッドMagicをPublicで追加しておきます
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Option Explicit Implements IJob Private parent_ As New ParentClass Private Property Get IJob_parent() As ParentClass Set IJob_parent = parent_ End Property Private Sub IJob_Attack() Debug.Print parent_.status.name & "は杖で攻撃した" End Sub Public Sub Magic(ByVal spell As String) Debug.Print parent_.status.name & "は" & spell & "を使った" End Sub |
・JobHealer 独自メソッドHealをPublicで追加しておきます
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Option Explicit Implements IJob Private parent_ As New ParentClass Private Property Get IJob_parent() As ParentClass Set IJob_parent = parent_ End Property Private Sub IJob_Attack() Debug.Print parent_.status.name & "は杖で攻撃した" End Sub Public Sub Heal(ByVal target As String) Debug.Print parent_.status.name & "は" & target & "を回復した" End Sub |
JobKnightとの違いはAttackの際に使う武器が「剣」ではなく「杖」になっている点と独自メソッドがPublicで定義されていることです。
最後に呼び出し処理を標準モジュールに作成します。
基本的には前回の記事と同様ですね。
異なる点は各playerの宣言はインターフェースクラスで行い、実装クラスとしてインスタンス生成している点と独自メソッドを呼び出す際は、実装クラスで宣言された変数にキャストするという2点です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
Option Explicit Sub test() Dim player1 As IJob Dim player2 As IJob Dim player3 As IJob ' ナイト Set player1 = createInstance(New JobKnight, "nao", 100) With player1 .Attack .parent.Defense End With ' 魔法使い Set player2 = createInstance(New JobMage, "Uta", 50) player2.Attack Dim mage As JobMage: Set mage = player2 mage.Magic "ファイア" ' ヒーラー Set player3 = createInstance(New JobHealer, "re", 60) player3.Attack Dim healer As JobHealer: Set healer = player3 healer.Heal player1.parent.status.name End Sub Private Function createInstance(job As IJob, _ ByVal name As String, ByVal hp As Long) With job.parent.status .name = name .hp = hp Debug.Print "name:" & .name & ", hp:" & .hp End With Set createInstance = job End Function |
実行結果も前回と同じです。
記述内容が増えたので、一見するとあまり良さがわからないかもしれません。しかし、上記のplayer1,2,3はIJobで宣言されているので、Object型のような万能型を使用しなくても、いずれもIJobとして渡すことができます。
実はcreateInstanceプロシージャがそのような実装となっています。New JobKnight, JobMage, JobHealer全て渡せて、かつ型安全で、もちろん自動メンバー表示も機能します。
また、Implementsを使うことでParentClassを介さなくてもAttackのポリモーフィズムが実現できているため、IJobという共通の型を持ちつつ、独自メソッドは適切に隠蔽するようになっています。(下図参照)


独自メソッドを呼び出す場合にキャストは必要なものの、型を明示して使う安全な実装になっています。厳格なコードを書きたい場合は、やはりこの方法がおすすめです。
少し癖があるので、あまり使われていない機能のように感じていますが、十分に活用する価値はあると思っています。
④まとめ
今回はVBAのImplementsについて紹介させていただきました。
やや使い方が難しいかもしれませんが、使いこなせるとかなり便利な機能だとは思いますので、クラスモジュールを使用されている方はぜひ使ってみてください。
以上、最後まで読んでいただき、ありがとうございました。
