実装/インターフェースとは何か疑問に思っている人がここに到着した場合に備えて、与えられた答えに対する理論的および実際的な貢献を以下に示します。
ご存知のように、VBA は継承をサポートしていないため、ほとんど盲目的にインターフェイスを使用して、異なるクラス間で共通のプロパティ/動作を実装することがあります。
それでも、後でなぜ重要なのかを理解するために、両者の概念的な違いを説明することは有益だと思います。
- 継承: is-a 関係 (正方形は形) を定義します。
- インターフェイス: 必須の関係を定義します (典型的な例は、
drawable
ドローアブル オブジェクトがメソッドを実装する必要があることを規定するインターフェイスですdraw
)。これは、異なるルート クラスに由来するクラスが共通の動作を実装できることを意味します。
継承とは、基本クラス (何らかの物理的または概念的なアーキタイプ) が拡張されることを意味しますが、インターフェイスは特定の動作を定義する一連のプロパティ/メソッドを実装します。
そのため、これは他のすべての図形が継承する基本クラスであり、すべての図形を描画可能にするインターフェイスを実装できるクラスであると言えます。このインターフェイスは、すべての Shape にメソッドがあり、図形を描画する方法/場所を指定することを保証するコントラクトです。円は、正方形とは異なる方法で描画される場合と、描画されない場合があります。
Shape
drawable
draw
クラス IDrawable:
'IDrawable interface, defining what methods drawable objects have access to
Public Function draw()
End Function
VBA は継承をサポートしていないため、特定のプロパティ/動作が一般的な形状 (正方形、円など) によって実装されることを保証するインターフェイス IShape を作成することを自動的に選択する必要があります。拡張できます。
クラス IShape:
'Get the area of a shape
Public Function getArea() As Double
End Function
問題になるのは、すべての Shape を drawable にしたいときです。
残念ながら、IShape はインターフェイスであり、VBA の基底クラスではないため、ドローアブル インターフェイスを基底クラスに実装することはできません。VBA では、あるインターフェイスに別のインターフェイスを実装させることはできないようです。これをテストした後、コンパイラは望ましい動作を提供していないようです。言い換えれば、IDrawable を IShape 内に実装することはできません。このため、IShape のインスタンスが IDrawable メソッドを強制的に実装することを期待しています。
IShape インターフェイスを実装するすべてのジェネリック シェイプ クラスにこのインターフェイスを実装する必要がありますが、幸いにも VBA では複数のインターフェイスを実装できます。
クラス cSquare:
Option Explicit
Implements iShape
Implements IDrawable
Private pWidth As Double
Private pHeight As Double
Private pPositionX As Double
Private pPositionY As Double
Public Function iShape_getArea() As Double
getArea = pWidth * pHeight
End Function
Public Function IDrawable_draw()
debug.print "Draw square method"
End Function
'Getters and setters
次の部分では、インターフェイスの一般的な使用方法と利点について説明します。
新しい正方形を返すファクトリを書くことからコードを始めましょう。(これは、引数をコンストラクターに直接送信できないことに対する単なる回避策です):
モジュール mFactory:
Public Function createSquare(width, height, x, y) As cSquare
Dim square As New cSquare
square.width = width
square.height = height
square.positionX = x
square.positionY = y
Set createSquare = square
End Function
メイン コードは、ファクトリを使用して新しい Square を作成します。
Dim square As cSquare
Set square = mFactory.createSquare(5, 5, 0, 0)
自由に使用できるメソッドを見ると、cSquare クラスで定義されているすべてのメソッドに論理的にアクセスできることがわかります。

なぜこれが重要なのかについては後で説明します。
ドローアブル オブジェクトのコレクションを本当に作成したい場合は、どうなるのだろうと思うはずです。あなたのアプリには、図形ではないが描画可能なオブジェクトが含まれている可能性があります。理論的には、描画可能な IComputer インターフェイス (クリップアートなど) を使用することを妨げるものは何もありません。
ドローアブル オブジェクトのコレクションが必要になる理由は、アプリのライフサイクルの特定の時点でそれらをループでレンダリングしたい場合があるからです。
この場合、コレクションをラップするデコレータ クラスを作成します (理由は後で説明します)。クラス collDrawables:
Option Explicit
Private pSize As Integer
Private pDrawables As Collection
'constructor
Public Sub class_initialize()
Set pDrawables = New Collection
End Sub
'Adds a drawable to the collection
Public Sub add(cDrawable As IDrawable)
pDrawables.add cDrawable
'Increase collection size
pSize = pSize + 1
End Sub
デコレータを使用すると、ネイティブの vba コレクションでは提供されないいくつかの便利なメソッドを追加できますが、ここでの実際のポイントは、コレクションが描画可能なオブジェクトのみを受け入れる (IDrawable インターフェイスを実装する) ことです。描画可能でないオブジェクトを追加しようとすると、タイプの不一致がスローされます (描画可能なオブジェクトのみが許可されます!)。
そのため、描画可能なオブジェクトのコレクションをループしてレンダリングしたい場合があります。描画不可能なオブジェクトをコレクションに入れると、バグが発生します。レンダリング ループは次のようになります。
Option Explicit
Public Sub app()
Dim obj As IDrawable
Dim square_1 As IDrawable
Dim square_2 As IDrawable
Dim computer As IDrawable
Dim person as cPerson 'Not drawable(!)
Dim collRender As New collDrawables
Set square_1 = mFactory.createSquare(5, 5, 0, 0)
Set square_2 = mFactory.createSquare(10, 5, 0, 0)
Set computer = mFactory.createComputer(20, 20)
collRender.add square_1
collRender.add square_2
collRender.add computer
'This is the loop, we are sure that all objects are drawable!
For Each obj In collRender.getDrawables
obj.draw
Next obj
End Sub
上記のコードは多くの透明性を追加することに注意してください: オブジェクトを IDrawable として宣言しました。これにより、コレクション内のすべてのオブジェクトで draw メソッドが使用できるため、ループが決して失敗しないことが透明になります。
Person をコレクションに追加しようとすると、この Person クラスがドローアブル インターフェイスを実装していないと、型の不一致がスローされます。
しかし、おそらく、オブジェクトをインターフェースとして宣言することが重要である最も関連性の高い理由は、インターフェースで定義されたメソッドのみを公開したいためであり、前に見たように個々のクラスで定義されたパブリック メソッドは公開したくないからです。 .
Dim square_1 As IDrawable

square_1 にメソッドがあることを確認するだけでなく、IDrawable によって定義されたメソッドのみdraw
が公開されることも確認します。
正方形の場合、これの利点はすぐには明らかではないかもしれませんが、より明確な Java コレクション フレームワークからの類推を見てみましょう。
IList
さまざまな種類のリストに適用できる一連のメソッドを定義する汎用インターフェイスが呼び出されているとします。リストの各タイプは、IList インターフェイスを実装する特定のクラスであり、独自の動作を定義し、場合によっては独自のメソッドを上に追加します。
次のようにリストを宣言します。
dim myList as IList 'Declare as the interface!
set myList = new ArrayList 'Implements the interface of IList only, ArrayList allows random (index-based) access
上記のコードでは、リストを IList として宣言することで、ArrayList 固有のメソッドを使用せず、インターフェイスによって規定されたメソッドのみを使用することが保証されます。次のようにリストを宣言したとします。
dim myList as ArrayList 'We don't want this
ArrayList クラスで明確に定義されているパブリック メソッドにアクセスできます。これが望ましい場合もありますが、多くの場合、クラス固有の public メソッドによって定義されたのではなく、内部クラスの動作を利用したいだけです。
この ArrayList をコードで 50 回以上使用すると、利点が明らかになり、LinkedList (このタイプのリストに関連する特定の内部動作を可能にする) を使用する方が良いことが突然わかります。
インターフェイスに準拠していれば、次の行を変更できます。
set myList = new ArrayList
に:
set myList = new LinkedList
インターフェイスがコントラクトが満たされていることを確認するため、他のコードは壊れません。IList で定義されたパブリック メソッドのみが使用されるため、さまざまな種類のリストを時間の経過とともに交換できます。
最後のこと (おそらく VBA ではあまり知られていない動作) は、インターフェイスに既定の実装を与えることができるということです。
次の方法でインターフェイスを定義できます。
IDrawable:
Public Function draw()
Debug.Print "Draw interface method"
End Function
draw メソッドも実装するクラス:
cSquare:
implements IDrawable
Public Function draw()
Debug.Print "Draw square method"
End Function
次の方法で実装を切り替えることができます。
Dim square_1 As IDrawable
Set square_1 = New IDrawable
square_1.draw 'Draw interface method
Set square_1 = New cSquare
square_1.draw 'Draw square method
変数を cSquare として宣言する場合、これは不可能です。
これが役立つ良い例をすぐに思いつくことはできませんが、テストすれば技術的に可能です。