J'essaie d'implémenter certaines formes pour un projet d'ingénierie et de les résumer pour certaines fonctions courantes afin de pouvoir avoir un programme généralisé.
Ce que j'essaie de faire, c'est d'avoir une interface appelée cShape
et d'avoir cRectangle
et cCircle
implémenter cShape
Mon code est ci-dessous:
cShape
interface
Option Explicit
Public Function getArea()
End Function
Public Function getInertiaX()
End Function
Public Function getInertiaY()
End Function
Public Function toString()
End Function
cRectangle
classe
Option Explicit
Implements cShape
Public myLength As Double ''going to treat length as d
Public myWidth As Double ''going to treat width as b
Public Function getArea()
getArea = myLength * myWidth
End Function
Public Function getInertiaX()
getInertiaX = (myWidth) * (myLength ^ 3)
End Function
Public Function getInertiaY()
getInertiaY = (myLength) * (myWidth ^ 3)
End Function
Public Function toString()
toString = "This is a " & myWidth & " by " & myLength & " rectangle."
End Function
cCircle
classe
Option Explicit
Implements cShape
Public myRadius As Double
Public Function getDiameter()
getDiameter = 2 * myRadius
End Function
Public Function getArea()
getArea = Application.WorksheetFunction.Pi() * (myRadius ^ 2)
End Function
''Inertia around the X axis
Public Function getInertiaX()
getInertiaX = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function
''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function getInertiaY()
getInertiaY = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function
Public Function toString()
toString = "This is a radius " & myRadius & " circle."
End Function
Le problème est que chaque fois que j'exécute mes cas de test, il se produit l'erreur suivante:
Erreur de compilation:
Le module objet doit implémenter '~' pour l'interface '~'
Il s'agit d'un concept ésotérique OOP et il y a un peu plus que vous devez faire et comprendre pour utiliser une collection personnalisée de formes.
Vous voudrez peut-être d'abord passer par this answer
pour obtenir une compréhension générale des classes et des interfaces dans VBA.
Ouvrez d'abord le Bloc-notes et copiez-collez le code ci-dessous
VERSION 1.0 CLASS
BEGIN
MultiUse = -1
END
Attribute VB_Name = "ShapesCollection"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Dim myCustomCollection As Collection
Private Sub Class_Initialize()
Set myCustomCollection = New Collection
End Sub
Public Sub Class_Terminate()
Set myCustomCollection = Nothing
End Sub
Public Sub Add(ByVal Item As Object)
myCustomCollection.Add Item
End Sub
Public Sub AddShapes(ParamArray arr() As Variant)
Dim v As Variant
For Each v In arr
myCustomCollection.Add v
Next
End Sub
Public Sub Remove(index As Variant)
myCustomCollection.Remove (index)
End Sub
Public Property Get Item(index As Long) As cShape
Set Item = myCustomCollection.Item(index)
End Property
Public Property Get Count() As Long
Count = myCustomCollection.Count
End Property
Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
Attribute NewEnum.VB_MemberFlags = "40"
Set NewEnum = myCustomCollection.[_NewEnum]
End Property
Enregistrez le fichier sous ShapesCollection.cls
sur votre bureau.
Assurez-vous de l'enregistrer avec l'extension
*.cls
Et nonShapesCollection.cls.txt
Maintenant ouvrez votre fichier Excel, allez sur VBE ALT+F11 et faites un clic droit dans le Project Explorer
. Sélectionnez Import File
dans le menu déroulant et accédez au fichier.
NB: Vous devez d'abord enregistrer le code dans un fichier
.cls
, puis l'importer car VBEditor ne vous permet pas d'utiliser les attributs. Les attributs vous permettent de spécifier le membre par défaut dans l'itération et d'utiliser le pour chaque boucle sur les classes de collection personnalisées
Voir plus:
Insérez maintenant 3 modules de classe. Renommez en conséquence et copiez-collez le code
cShape c'est votre interface
Public Function GetArea() As Double
End Function
Public Function GetInertiaX() As Double
End Function
Public Function GetInertiaY() As Double
End Function
Public Function ToString() As String
End Function
cCircle
Option Explicit
Implements cShape
Public Radius As Double
Public Function GetDiameter() As Double
GetDiameter = 2 * Radius
End Function
Public Function GetArea() As Double
GetArea = Application.WorksheetFunction.Pi() * (Radius ^ 2)
End Function
''Inertia around the X axis
Public Function GetInertiaX() As Double
GetInertiaX = Application.WorksheetFunction.Pi() / 4 * (Radius ^ 4)
End Function
''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function GetInertiaY() As Double
GetInertiaY = Application.WorksheetFunction.Pi() / 4 * (Radius ^ 4)
End Function
Public Function ToString() As String
ToString = "This is a radius " & Radius & " circle."
End Function
'interface functions
Private Function cShape_getArea() As Double
cShape_getArea = GetArea
End Function
Private Function cShape_getInertiaX() As Double
cShape_getInertiaX = GetInertiaX
End Function
Private Function cShape_getInertiaY() As Double
cShape_getInertiaY = GetInertiaY
End Function
Private Function cShape_toString() As String
cShape_toString = ToString
End Function
cRectangle
Option Explicit
Implements cShape
Public Length As Double ''going to treat length as d
Public Width As Double ''going to treat width as b
Public Function GetArea() As Double
GetArea = Length * Width
End Function
Public Function GetInertiaX() As Double
GetInertiaX = (Width) * (Length ^ 3)
End Function
Public Function GetInertiaY() As Double
GetInertiaY = (Length) * (Width ^ 3)
End Function
Public Function ToString() As String
ToString = "This is a " & Width & " by " & Length & " rectangle."
End Function
' interface properties
Private Function cShape_getArea() As Double
cShape_getArea = GetArea
End Function
Private Function cShape_getInertiaX() As Double
cShape_getInertiaX = GetInertiaX
End Function
Private Function cShape_getInertiaY() As Double
cShape_getInertiaY = GetInertiaY
End Function
Private Function cShape_toString() As String
cShape_toString = ToString
End Function
Vous devez Insert
un standard Module
maintenant et copier-coller le code ci-dessous
Module1
Option Explicit
Sub Main()
Dim shapes As ShapesCollection
Set shapes = New ShapesCollection
AddShapesTo shapes
Dim iShape As cShape
For Each iShape In shapes
'If TypeOf iShape Is cCircle Then
Debug.Print iShape.ToString, "Area: " & iShape.GetArea, "InertiaX: " & iShape.GetInertiaX, "InertiaY:" & iShape.GetInertiaY
'End If
Next
End Sub
Private Sub AddShapesTo(ByRef shapes As ShapesCollection)
Dim c1 As New cCircle
c1.Radius = 10.5
Dim c2 As New cCircle
c2.Radius = 78.265
Dim r1 As New cRectangle
r1.Length = 80.87
r1.Width = 20.6
Dim r2 As New cRectangle
r2.Length = 12.14
r2.Width = 40.74
shapes.AddShapes c1, c2, r1, r2
End Sub
Exécutez le Main
Sub et vérifiez les résultats dans le Immediate Window
CTRL+G
Dans votre module de classe ShapesCollection
, il y a 2 sous-marins pour ajouter des éléments à la collection.
La première méthode Public Sub Add(ByVal Item As Object)
prend simplement une instance de classe et l'ajoute à la collection. Vous pouvez l'utiliser dans votre Module1
comme ceci
Dim c1 As New cCircle
shapes.Add c1
La fonction Public Sub AddShapes(ParamArray arr() As Variant)
vous permet d'ajouter plusieurs objets en même temps en les séparant par une virgule ,
De la même manière que la sous-fonction AddShapes()
.
C'est un meilleur design que d'ajouter chaque objet séparément, mais c'est à vous de choisir celui que vous allez choisir.
Remarquez comment j'ai commenté du code dans la boucle
Dim iShape As cShape
For Each iShape In shapes
'If TypeOf iShape Is cCircle Then
Debug.Print iShape.ToString, "Area: " & iShape.GetArea, "InertiaX: " & iShape.GetInertiaX, "InertiaY:" & iShape.GetInertiaY
'End If
Next
Si vous supprimez les commentaires des lignes 'If
Et 'End If
, Vous ne pourrez imprimer que les objets cCircle
. Ce serait vraiment utile si vous pouviez utiliser des délégués dans VBA mais vous ne pouvez pas, donc je vous ai montré l'autre façon d'imprimer un seul type d'objets. Vous pouvez évidemment modifier l'instruction If
pour l'adapter à vos besoins ou simplement imprimer tous les objets. Encore une fois, c'est à vous de décider comment vous allez gérer vos données :)
Il existe deux ajouts non documentés sur VBA et l'instruction "Implements".
VBA ne prend pas en charge le caractère de non-décompression "_" dans le nom d'une méthode d'une interface héritée d'une classe dérivée. F.e. il ne compilera pas de code avec une méthode telle que cShape.get_area (testé sous Excel 2007): VBA générera l'erreur de compilation ci-dessus pour toute classe dérivée.
Si une classe dérivée n'implémente pas la propre méthode nommée comme dans l'interface, VBA compile un code avec succès, mais la méthode sera inaccessible via une variable du type de classe dérivée.
Nous devons implémenter toutes les méthodes d'interface dans la classe qui est utilisée.
classe de cercle
Option Explicit
Implements cShape
Public myRadius As Double
Public Function getDiameter()
getDiameter = 2 * myRadius
End Function
Public Function getArea()
getArea = Application.WorksheetFunction.Pi() * (myRadius ^ 2)
End Function
''Inertia around the X axis
Public Function getInertiaX()
getInertiaX = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function
''Inertia around the Y axis
''Ix = Iy in a circle, technically should use same function
Public Function getIntertiaY()
getIntertiaY = Application.WorksheetFunction.Pi() / 4 * (myRadius ^ 4)
End Function
Public Function toString()
toString = "This is a radius " & myRadius & " circle."
End Function
Private Function cShape_getArea() As Variant
End Function
Private Function cShape_getInertiaX() As Variant
End Function
Private Function cShape_getIntertiaY() As Variant
End Function
Private Function cShape_toString() As Variant
End Function
Classe Rectangle
Option Explicit
Implements cShape
Public myLength As Double ''going to treat length as d
Public myWidth As Double ''going to treat width as b
Private getIntertiaX As Double
Public Function getArea()
getArea = myLength * myWidth
End Function
Public Function getInertiaX()
getIntertiaX = (myWidth) * (myLength ^ 3)
End Function
Public Function getIntertiaY()
getIntertiaY = (myLength) * (myWidth ^ 3)
End Function
Public Function toString()
toString = "This is a " & myWidth & " by " & myLength & " rectangle."
End Function
Private Function cShape_getArea() As Variant
End Function
Private Function cShape_getInertiaX() As Variant
End Function
Private Function cShape_getIntertiaY() As Variant
End Function
Private Function cShape_toString() As Variant
End Function
classe cSharp
Option Explicit
Public Function getArea()
End Function
Public Function getInertiaX()
End Function
Public Function getIntertiaY()
End Function
Public Function toString()
End Function
Si l'interface ISomeInterface
a:
Public Sub someMethod()
' Interface, no code
End Sub
Alors le implémentation doit être comme:
Implements ISomeInterface
Public Sub ISomeInterface_someMethod()
' ^^^^^^^^^^^^^^^ ' If missing: Compile Error
' Code goes here
End Sub
Une belle approche:
Implements ISomeInterface
Private Sub someMethod()
' Business logic goes here
End Sub
Public Sub ISomeInterface_someMethod()
someMethod ' i.e. Business logic in 1 place: someMethod
End Sub
Cela dit, les autres réponses méritent d'être lues.
Article très intéressant pour comprendre simplement pourquoi et quand une interface peut être utile! Mais je pense que votre dernier exemple sur l'implémentation par défaut est incorrect. Le premier appel à la méthode draw
de square_1 instancié comme IDrawable imprime correctement le résultat que vous donnez, mais le deuxième appel à la méthode draw
de square_1 instancié comme cSquare est incorrect, rien n'est imprimé. 3 méthodes différentes entrent en jeu:
IDrawable.cls:
Public Function draw()
Debug.Print "Interface Draw method"
End Function
cSquare.cls:
Implements IDrawable
Public Function draw()
Debug.Print "Class Draw method"
End Function
Public Function IDrawable_draw()
Debug.Print "Interfaced Draw method"
End Function
Module standard:
Sub Main()
Dim square_1 As IDrawable
Set square_1 = New IDrawable
Debug.Print "square_1 : ";
square_1.draw
Dim square_2 As cSquare
Set square_2 = New cSquare
Debug.Print "square_2 : ";
square_2.draw
Dim square_3 As IDrawable
Set square_3 = New cSquare
Debug.Print "square_3 : ";
square_3.draw
End Sub
Résulte en:
square_1 : Interface Draw method
square_2 : Class Draw method
square_3 : Interfaced Draw method