La définition en BASIC de modules de classes est parfaitement possible comme cela l'est en Visual Basic.
Le niveau de sophistication de ces modules et leur syntaxe est comparable à ce qu'ils sont en VB6.
La suite de ce sujet en illustre les possibilités par quelques exemples.
Comment procéder ?
Définition d'une classe :
Code : Tout sélectionner
Option Compatible
Option ClassModule
Option Explicit
REM -----------------------------------------------------------------------------------------------------------------------
REM --- Internal Variables ---
REM -----------------------------------------------------------------------------------------------------------------------
Private _X As Double
Private _Y As Double
REM -----------------------------------------------------------------------------------------------------------------------
REM --- CONSTRUCTORS / DESTRUCTORS ---
REM -----------------------------------------------------------------------------------------------------------------------
Private Sub Class_Initialize()
' Default point = Origin of axis
_X = 0
_Y = 0
End Sub ' Constructor
REM -----------------------------------------------------------------------------------------------------------------------
Private Sub Class_Terminate()
_X = - 4.94065645841247E-324 ' Most negative value allowed for Double variables
_Y = - 4.94065645841247E-324
end sub
REM -----------------------------------------------------------------------------------------------------------------------
REM --- CLASS GET/LET/SET PROPERTIES ---
REM -----------------------------------------------------------------------------------------------------------------------
Public Property Get X As Double
X = _X
End Property
Public Property Let X(ByVal pValue As Double)
_X = pValue
End property
REM -----------------------------------------------------------------------------------------------------------------------
Public Property Get Y As Double
Y = _Y
End Property
Public Property Let Y(ByVal pValue As Double)
_Y = pValue
End property
REM -----------------------------------------------------------------------------------------------------------------------
Public Property Get Dummy As Variant
Dummy = Null
End Property
REM -----------------------------------------------------------------------------------------------------------------------
REM --- CLASS METHODS ---
REM -----------------------------------------------------------------------------------------------------------------------
Public Sub Move(ByVal pX As Double, ByVal pY As Double)
_X = _X + pX
_Y = _Y + pY
End Sub
Les lignes « Option Compatible » et « Option ClassModule » en tête du module sont celles qui déterminent la caractéristique du module, à savoir qu'il s'agit d'un module de classe. Elles doivent se trouver dans l'ordre indiqué. Dans le cas contraire, la source ne passera pas la compilation.
Un module de classe contient un certain nombre de Variables Internes. Elles sont regroupées en-dehors des function/sub et reçoivent généralement l'attribut « Private ». Il signifie que la variable interne ne peut être utilisée que dans le module même, contrairement à la valeur « Public ». A noter que cet attribut est ignoré en Basic. Les variables concernées peuvent être de tout type, y compris une autre classe. Un exemple sera donné plus bas. Une variable interne peut également contenir un objet UNO.
Les Sub « Class_Initialize » et « Class_Terminate » sont resp. Les constructeur et destructeur de la classe. Elles sont exécutées, pour la première, avant la première utilisation d'une instance de classe, pour la seconde, lors de sa suppression (voir plus bas).
Les propriétés sont déterminées par les définitions « Property Get », « Property Let » et « Property Set ». « Get » fournit la valeur de la propriété, « Let » correspond à une assignation classique, « Set » permet la transmission d'objets ou de structures complexes. Notez que la valeur retournée par « Property Get » ainsi que l'argument de « Property Let » doivent être de TYPE IDENTIQUE.
A noter que l'instruction « Exit » s'enrichit d'une option « Exit Property ».
Les méthodes sont des Sub ou des Function. Une Sub exécute une action quelconque et représente le cas le plus fréquent d'une méthode.
Utilisation de la classe « Point »:
La déclaration d'une instance de classe se fait par l'instruction « Dim ». La variable associée sera déclarée comme « Object ».
L'instruction « Set » crée l'instance et exécute le constructeur.
Code : Tout sélectionner
Dim a As Object
Set a = New Point
a.X = 4
a.Y = 1
MsgBox a._X
Toutefois il faut comprendre que le constructeur ne sera exécuté qu'à la première utilisation de la variable « a », soit à l'instruction « a.X = 4 ». Ceci signifie que pour chaque instruction entre la déclaration de la variable et sa première utilisation l'interpréteur insérera du code pour savoir s'il y a lieu de déclencher le constructeur.
Personnellement je privilégierai en conséquence la forme précédente.
Code : Tout sélectionner
Dim a As New Point
a.X = 4
a.Y = 1
MsgBox a._X
Code : Tout sélectionner
Option Compatible
Option ClassModule
Option Explicit
REM -----------------------------------------------------------------------------------------------------------------------
REM --- Internal Variables ---
REM -----------------------------------------------------------------------------------------------------------------------
Private _R As Double
Private _Center As Object
REM -----------------------------------------------------------------------------------------------------------------------
REM --- CONSTRUCTORS / DESTRUCTORS ---
REM -----------------------------------------------------------------------------------------------------------------------
Private Sub Class_Initialize()
' Default circle = Null circle at axis origin
_R = 0
Set _Center = New Point
End Sub ' Constructor
REM -----------------------------------------------------------------------------------------------------------------------
Private Sub Class_Terminate()
_R = 0
Erase _Center
End Sub
REM -----------------------------------------------------------------------------------------------------------------------
REM --- CLASS GET/LET/SET PROPERTIES ---
REM -----------------------------------------------------------------------------------------------------------------------
Property Get R As Double
R = _R
End Property
Public Property Let R(ByVal pValue As Double)
_R = pValue
End property
REM -----------------------------------------------------------------------------------------------------------------------
Public Property Get Center As Object
Set Center = _Center
End Property
Property Set Center(ByVal pValue As Object)
Set _Center = pValue
End property
REM -----------------------------------------------------------------------------------------------------------------------
Public Function Area() As Double
Area = PI() * R * R
End Function
Public Function Circumference() As Double
Circumference = 2 * PI() * R
End Function
REM -----------------------------------------------------------------------------------------------------------------------
Public Property Get Dummy As Variant
Dummy = Null
End Property
REM -----------------------------------------------------------------------------------------------------------------------
REM --- CLASS METHODS ---
REM -----------------------------------------------------------------------------------------------------------------------
Public Sub MoveHoriz(ByVal pValue) ' pValue might be negative
_Center.Move(pValue, 0)
End Sub
Public Sub MoveVert(ByVal pValue) ' pValue might be negative
_Center.Move(0, pValue)
End Sub
REM -----------------------------------------------------------------------------------------------------------------------
Public Function IntersectX() As Variant
' Return intersection points of circle with X-axis
' Substitute y = 0 in circle equation (x-Cx)2 + (y-Cy)2 = R2
' => Equation of degree 2 with unknown x
Dim a As Double, b As Double, c As Double, dDiscr As Double
a = 1
b = -2 * _Center.X
c = (_Center.X ^ 2) + (_Center.Y ^ 2) - (R ^ 2)
dDiscr = (b ^ 2) - (4 * a * c)
Dim oInterSect1 As Object, oInterSect2 As Object
Select Case True
Case dDiscr < 0 : InterSectX = Array()
Case dDiscr = 0
Set oInterSect1 = New Point
oInterSect.X = -b / (2 * a)
Set InterSectX = Array(oInterSect)
Case Else
Set oInterSect1 = New Point
Set oInterSect2 = New Point
oInterSect1.X = (-b - Sqr(dDiscr)) / (2 * a)
oInterSect2.X = (-b + Sqr(dDiscr)) / (2 * a)
InterSectX = Array(oInterSect1, oInterSect2)
End Select
End Function
Une des variables internes est une instance de la classe « Point ».
Une function, telle « Area », outre les actions qu'elle peut exécuter, retourne de surcroît une valeur. Quelle est la différence entre « Property Get Area() » et « Public Function Area() » ? Il n'y en a pas sauf en ce qui concerne leur traitement par le debugger. Voir plus bas.
Le destructeur « Class_Terminate » contient une instruction « Erase » qui, elle-même, active le destructeur de l'objet « _Center » de type « Point ».
Les méthodes « MoveHoriz » et « MoveVert » font appel à la méthode « Move » de la classe « Point » appliquée à la variable interne « _Center ».
Exemples :
- Avec illustration de l'instruction « With » ainsi que des propriétés en cascade « b.Center.X » :
Code : Tout sélectionner
Dim a As Object
Set a = New Point
a.X = 2
a.Y = 1
Dim b As Object
Set b = New Circle
With b
.R = 2
Set .Center = a
.MoveHoriz(2) ' Center becomes (4,1)
MsgBox "Circle centered on (" & .Center.X & "," & .Center.Y & "). Circumference = " & .Circumference
End With
Erase b
Code : Tout sélectionner
Dim b As Object
Set b = New Circle
b.Center.X = 4
b.Center.Y = 1
b.R = 2
Dim vPoints As Variant, i As Integer
vPoints = b.IntersectX() ' Return an array of Points objects
For i = LBound(vPoints) To UBound(vPoints)
MsgBox vPoints(i).X
Next i
L'utilisation de Xray est inutile. En effet aucune information utile sur une instance de classe ne proviendra de cet outil. Cependant son exécution sur un tel objet se passe sans douleur.
Le comportement du debugger de l'IDE est différent. Soit l'exécution se trouve à l'intérieur de la classe, dans ce cas les variables internes de l'instance (en plus des variables locales de la routine en cours ...) sont visualisables en mentionnant simplement leur nom. Soit l'exécution est en-dehors de la classe, dans ce cas la visualisation d'une variable passera par le calcul par le debugger de chaque propriété de la classe ! Super, car même si une des propriétés est elle-même une instance de classe, l'arborescence est bien gérée.
Toutefois ceci signifie :
- que si le calcul de la propriété contient une erreur, le debugger commettra la même erreur, d'où l'intérêt parfois d'utiliser temporairement pendant la mise au point « Function » plutôt que « Property Get »;
- que si vous êtes en mode pas-à-pas, le pas-à-pas s'engage à son tour et sous l'impulsion du debugger sur la voie des différentes « Property Get » ..., ce qui entraîne à coup sûr et rapidement un crash de AOO/LibO.
La propriété « Dummy » a été insérée dans chacun des modules de classe simplement parce que le debugger affiche dans l'IDE toutes les propriétés, sauf, bizarrement, la dernière ... ?
En conclusion : visualiser une variable de classe, oui, mais si le calcul des propriétés est au point, et, une fois visualisée, retirer la variable des variables observées avant de poursuivre l'exécution.
Autres remarques :
Basic supporte également le verbe « Implements » comme en VB6. Son usage me paraît cependant peu utile.
VB6 accepte également l'usage dans un module de classe du mot-clé « This » pour désigner l'instance courante en exécution. De plus il permet le passage de l'objet traité comme argument d'une fonction externe au module. Ceci n'est pas possible en Basic, à ma connaissance.
L'héritage entre classes d'objets n'est pas possible. Le verbe « Inherits » n'est implémenté d'ailleurs en VB que dans les versions postérieures à VB6.
J'espère que ces explications vous seront profitables ...
JPL