VB6.0 をある程度使いこなしている方はご存知かと思いますが、VB6.0 でもポリモーフィズム (多態性) を表現することができます。といっても、案外知られていないですし、まったく活用されていないと思います。少なくとも、他人のソースでお目にかかったことはありません。
とはいえ、ネタもないので説明だけしようと思います。
まず、単純な「動物」を示す IAnimal インターフェイスを定義します。プロジェクトにクラス モジュールを追加し、IAnimal というクラス名をつけ以下のように記述します。
VB6.0 - IAnimal インターフェイス
'/** ICry インターフェイス */
Option Explicit
' Cry 仮想メソッド
Public Sub Cry()
End Sub
これだけで完了です。「泣く」という動作を示す Cry メソッドは「仮想メソッド」になりますので、実装部分のコードの記述はありません。いわゆる空実装ということもあって、インターフェイスというよりは抽象クラスに近いイメージですね。プロパティ ウィンドウで指定したクラス モジュール名が「型」になります。
次に、IAnimal を実装したクラス モジュールを書きます。インターフェイスを実装するには、Implements キーワードをクラスの先頭に記述します。ここでは、身近な動物として Dog (犬) クラスと Cat (猫) クラスとしました。
VB6.0 - Dog クラス
'/** Dog クラス */
Option Explicit
' IAnimal インターフェイスを実装していることを定義します
Implements IAnimal
' IAnimal.Cry メソッドの実装
Private Sub IAnimal_Cry()
Call MsgBox("ワン! ワン!")
End Sub
VB6.0 - Cat クラス
'/** Cat クラス */
Option Explicit
' IAnimal インターフェイスを実装していることを定義します
Implements IAnimal
' IAnimal.Cry メソッドの実装
Private Sub IAnimal_Cry()
Call MsgBox("にゃーん")
End Sub
Dog クラスおよび、Cat クラスは、IAnimal インターフェイスを実装するので Cry メソッドの実装が必須となります。VB6.0 では、[インターフェイス名]_[メソッド名] という名前で識別しているので、今回は IAnimal_Cry という名前のメソッドが必要になるということです。
このメソッドのアクセシビリティが Private であることに着目してください。これでは外部から呼び出せないですね。というより、呼び出せないようにしました。といっても、この「具象化したクラスの型」からは呼び出せないだけです。インターフェイスの型からは呼び出すことができます。
実際に「インターフェイスの型」からこの Cry メソッドを呼び出してみましょう。適当なところで、以下のように記述します。
VB6.0 - IAnimal インターフェイスから Cry メソッドを呼び出す
'/** テスト用のモジュールか何か */
Option Explicit
' テスト用の MosaMosaAA メソッド
Private Sub MosaMosaAA()
Dim oneDog As IAnimal
Set oneDog = New Dog
Call oneDog.Cry()
Dim oneCat As IAnimal
Set oneCat = New Cat
Call oneCat.Cry()
End Sub
実行結果として、メッセージ ボックスに「ワン! ワン!」と「にゃーん」が出力されます。
Dog クラス、または Cat クラスの Cry メソッドは Private になっていますが、IAnimal 型から間接的に呼び出されているため問題なく実行されます。oneDog も oneCat も型は IAnimal ですが、Cry メソッドを呼び出すとそれぞれ別の動作をしました。一般的には、これを「ポリモーフィズム (多態性)」と呼んでいます。
別の動作をするのは、IAnimal 型という変数の入れ物に入ったデータが、実際には Dog 型だったり、Cat 型だったりするからです。変数という入れ物の型とは別に、それに格納されているデータの型について意識する必要があります。
変数の入れ物の型はデータの型と一致していないため曖昧になりますが、この型からメソッド (メンバ) を呼び出したとしても「遅延バインディング」にはなりません。確かにインターフェイスによる抽象化は、メソッドの具体的な振る舞いは曖昧になっています。しかし、どのメンバが実装されているかは明示化されているため、コンパイル解決できるのです。
では、どういった時に活用すれば良いのでしょうか? 私の場合は、印刷処理をそれなりにフレームワーク化したい時に使いました。印刷するためにはデータを集める必要があるのですが、印刷する処理によって集めるべきデータが全然違います。しかし、印刷処理を記述する部分は冗長がないようにしたい。分岐すれば良いだけのことだが、呼び出し元ではそんなことは意識させたくない。そんな時に使用しました。あとは、データクラスを作ってそのデータを実際のデータベースに更新したい時にも使いましたね。