Developpez.com

Plus de 2 000 forums
et jusqu'à 5 000 nouveaux messages par jour

Programmation Orientée Objet en VBA

Le , par =JBO=, Membre émérite
Bonjour,

En fait le fil de messages originel est à rechercher sur le forum Access/VBA Access qui évoquait la problématique de l'héritage pour les classes développées en VBA: Héritage, possible ou non?

Donc la discussion a quelque peu dévié sur la question du polymorphisme, et s'est retrouvé déplacée sur le forum Général VBA.

_____---====OOOOO====---

Au risque de déborder un peu sur le sujet originel, je souhaite préciser comment on met en œuvre le polymorphisme en VB/VBA.

Citation Envoyé par Pierre Fauconnier  Voir le message
Ce ne serait pas l'inverse ? ... Implements permet "une sorte" d'héritage, mais pas le polymorphisme... (enfin, c'est ce qu'il me semble...)

D'abord il faut s'entendre sur la signification de polymorphisme.
Aussi, je me reposerai sur Wikipédia avec un court extrait ci-dessous et la suite sur la page de Wikipédia illustrée par un exemple:
Citation Envoyé par Wikipédia
En informatique, le polymorphisme est l'idée d'autoriser le même code à être utilisé avec différents types, ce qui permet des implémentations plus abstraites et générales.

Je reprends l'exemple de la page de Wikipédia (merci de vous y reporter) en l'adaptant au VB/VBA.

Une classe-interface cForme décrit les méthodes à implémenter pour toutes les "formes" géométriques:
Code : Sélectionner tout
1
2
3
4
5
Option Explicit 
 
Public Function Aire() As Double 
    Aire = 0 
End Function
Une classe cCarré décrit les formes de type Carré et implémente les méthodes d'une "forme" géométrique.

L'utilisation du mot-clé Implements (dans la partie déclaration du module de classe) spécifie qu'une classe implémente l'interface publique (méthodes et propriétés) d'une autre classe.
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Option Explicit 
 
Implements cForme 
 
Private p_nCôté As Double 
 
Private Function cForme_Aire() As Double 
    cForme_Aire = p_nCôté * p_nCôté 
End Function 
 
Public Property Get Côté() As Double 
    Côté = p_nCôté 
End Property 
 
Public Property Let Côté(n As Double) 
    p_nCôté = n 
End Property
Une classe cCercle décrit les formes de type Cercle et implémente les méthodes d'une "forme" géométrique:
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Option Explicit 
 
Implements cForme 
 
Private p_nRayon As Double 
 
Private Function cForme_Aire() As Double 
    cForme_Aire = CDbl(3.1415926535) * p_nRayon * p_nRayon 
End Function 
 
Public Property Get Rayon() As Double 
    Rayon = p_nRayon 
End Property 
 
Public Property Let Rayon(n As Double) 
    p_nRayon = n 
End Property
Dans un module de code, on spécifie la fonction AireTotal() qui reçoit en paramètre une collection d'objets "formes" et retourne la somme des aires de ces objets.
C'est ici que le polymorphisme rentre en jeu puisque, selon le type de "forme", la fonction fait toujours appel à la bonne méthode de calcul Aire(). Tout type de "forme" géométrique qui implémente la classe/interface cForme peut être ajouté sans nécessiter une modification de la fonction AireTotal().
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
Public Function AireTotal(collFormes As Collection) As Double 
    Dim oForme As cForme 
    Dim nTotal As Double 
     
    nTotal = 0 
     
    For Each oForme In collFormes 
        ' la fonction "sait" automatiquement quelle méthode Aire() appeler 
        nTotal = nTotal + oForme.Aire() 
    Next oForme 
     
    AireTotal = nTotal 
End Function
Enfin, on peut aussi coder une procédure pour tester tout ça:
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Public Sub TestFormes() 
    Dim oCarré As New cCarré 
    Dim oCercle As New cCercle 
    Dim collFormes As New Collection 
    Dim nTotal As Double 
     
    oCarré.Côté = 3.4 
    oCercle.Rayon = 5.1 
     
    collFormes.Add oCarré 
    collFormes.Add oCercle 
     
    nTotal = AireTotal(collFormes) 
 
    Debug.Print nTotal 
End Sub
A l'usage, l'implémentation d'interface permet:
(1) de coder des procédures "génériques" qu'il ne sera pas nécessaire de modifier pour prendre en compte de nouveaux "sous-types" (exemple la fonction AireTotal()),
(2) d'obtenir un code plus robuste parce que les appels de méthodes sont vérifiés à la compilation (liaison dynamique "late binding" mais avec la rigueur du contrôle de type).

Merci aux courageux et curieux qui ont lu jusqu'au bout.
_


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de Pierre.M Pierre.M - Membre à l'essai http://www.developpez.com
le 13/11/2008 à 18:55
mais le VBS ne connait que le type Variant il me semble

Oui. En fait les "autres types" sont des sous-types de Variant, autant pour moi je me suis mal exprime

Voila pour la paranthese VBS!
Avatar de Tonioyo Tonioyo - Membre confirmé http://www.developpez.com
le 17/04/2009 à 17:21
Bonjour,

Ce débat est très intéressant en effet mais les applications à ce concept dépasse largement l'utilisation des interfaces graphiques.
Pour ma part, il est bien question de polymorphisme à une exception près, c'est cela qui fait que ce n'est pas du vrai polymorphisme de mon point de vue.

Une classe mamifère contient des propriétés et méthodes définissant un mamifère.
Une classe Lapin contient les propriétés et méthodes pour un lapin
Une classe Raton-Laveur contient les propriétés et méthodes d'un raton laveur.

Avec Implements Un Lapin est un mamifère et un Raton-Laveur l'est aussi, jusque là ok.

mais lorsque l'on dis : J'ai un mamifère (implémentation / New), c'est un lapin (Affectation de notre mamifère comme lapin). Si je veux accèder aux propriétés et méthodes de mon mamifère en VBA (tout du moins Access VBA), on a accès qu'aux propriétés et méthodes de notre mamifère ce qui est juste et faux à la fois car à partir de mamifère on devrait avoir accès à la partie mamifère et à la partie Lapin. (Tout du moins de mémoire cela fonctionne comme ça en C++)

Pour synthétiser lorsque l'on s'adresse à un mamifère quelque soit sont type plus précis on accède qu'à l'implémentation Mamifère de notre Objet (d'où le nom du mot clé Implément).

De manière générale, on peu considérer ça comme du polymorphisme.

C'est important car l'impact sur les moyennes - grosses applis permet un gros gains d'organisation et de clareté dans un code bien écrit
Avatar de Pierre Fauconnier Pierre Fauconnier - Responsable Office & Excel http://www.developpez.com
le 17/04/2009 à 21:41
En ce qui me concerne, j'ai trouvé avec l'implémentation de quoi faciliter la gestion des formulaires en Access, grâce aux procédures génériques implémentées...
Avatar de =JBO= =JBO= - Membre émérite http://www.developpez.com
le 07/05/2009 à 14:00
Bonjour,

J'ai un peu phosphoré à la question soulevée par Tonioyo: comment accéder aux méthodes/propriétés de la classe spécifique ?
Citation Envoyé par Tonioyo  Voir le message
Avec Implements Un Lapin est un mamifère et un Raton-Laveur l'est aussi, jusque là ok.

mais lorsque l'on dis : J'ai un mamifère (implémentation / New), c'est un lapin (Affectation de notre mamifère comme lapin). Si je veux accèder aux propriétés et méthodes de mon mamifère en VBA (tout du moins Access VBA), on a accès qu'aux propriétés et méthodes de notre mamifère ce qui est juste et faux à la fois car à partir de mamifère on devrait avoir accès à la partie mamifère et à la partie Lapin.

En VBA, il faudrait poser le problème en passant par le type Object qui donne accès aux méthodes/propriétés de la classe instanciée (celle à laquelle s'applique New).

J'ai un animal (Object) et c'est un lapin (New cLapin).
Code VBA : Sélectionner tout
1
2
Dim oAnimal As Object 
Set oAnimal = New cLapin
Si cet animal est effectivement un mammifère, alors on peut accéder aux méthodes/propriétés de mammifère que sa classe implémente.
Code VBA : Sélectionner tout
1
2
3
4
5
6
7
8
9
Dim oMammifère As iMammifère 
  
If (TypeOf oAnimal Is iMammifère) Then 
    ' cet animal est un mammifère 
    Set oMammifère = oAnimal 
  
    MsgBox "Ce mammifère est un: " & TypeName(oMammifère) & vbCrLf & _ 
            "Voilà son cri: " & oMammifère.Cri 
End If
Via la référence d'objet oAnimal, on peut aussi accéder aux méthodes/propriétés spécifiques de la classe cLapin:
Code : Sélectionner tout
1
2
3
 
' Le lapin peut bondir (méthode de la classe cLapin) 
oAnimal.Bondir
Avatar de JP30 JP30 - Futur Membre du Club http://www.developpez.com
le 16/07/2009 à 7:20
Salut à tous,
Quelle passionnante discussion !
Mais ces 2 fonctions MsExcel en sont-elles des exemples?
Code : Sélectionner tout
1
2
INDEX("Matrice";NumLigne;NumCol) 
INDEX("Réf";NumLigne;NumCol;NumZone)
Code : Sélectionner tout
1
2
RECHERCHE("ValCherchée";"PlageRecherche";"PlageRésultat") 
RECHERCHE("ValCherchée";"Matrice")
Si oui, Polymorphisme ou Implémentation?
à+
Avatar de Pierre Fauconnier Pierre Fauconnier - Responsable Office & Excel http://www.developpez.com
le 14/10/2009 à 12:06
Tiens, je n'avais pas vu cette intervention sur RECHERCHE

Pour moi, c'est du polymorphisme
Avatar de Arkham46 Arkham46 - Responsable Access http://www.developpez.com
le 19/11/2009 à 17:23
Bjr,

Je reviens un peu sur la limitation de ce polymorphisme.
Je parle du fait qu'on ne peut pas partager une méthode de la classe mère.

Je déroule un petit exemple à base de client et fournisseur :
- Je souhaite pouvoir définir un nom et un prénom à mes fournisseur.
- Je souhaite également avoir une fonction qui me donne le nom complet (concaténation de prénom + nom).
- je ne veux pas avoir à maintenir cette fonction à plusieurs endroits car c'est la même pour tout mes types de contacts
- par contre je veux pouvoir définir des méthodes qui vont s'exécuter différemment pour chaque type de contact : par exemple une méthode qui ajoute le contact dans la bonne table (et avec les bons champs) en fonction de son type.

Je crée donc une classe d'interface clContact :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
 
Public ContactName As String 
Public ContactFirstName As String 
 
Public Function FullName() As String 
FullName = ContactFirstName & " " & ContactName 
End Function 
 
Public Sub Insert() 
End Sub
Cette classe :
- stocke le nom (ContactName) et le prénom (ContactFirstName)
- contient la fonction FullName qui concatène prénom et nom
- la fonction Insert qui est vide car elle sera créée dans chaque "sous-classe"

Voici la classe clClient :
Code : Sélectionner tout
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
 
Implements clContact 
Private Parent As clContact 
 
Private Sub Class_Initialize() 
Set Parent = New clContact 
End Sub 
 
Private Property Get clContact_ContactFirstName() As String 
clContact_ContactFirstName = Parent.ContactFirstName 
End Property 
 
Private Property Let clContact_ContactFirstName(ByVal RHS As String) 
Parent.ContactFirstName = RHS 
End Property 
 
Private Property Let clContact_ContactName(ByVal RHS As String) 
Parent.ContactName = RHS 
End Property 
 
Private Property Get clContact_ContactName() As String 
clContact_ContactName = Parent.ContactName 
End Property 
 
Private Function clContact_FullName() As String 
clContact_FullName = Parent.FullName 
End Function 
 
Private Sub clContact_Insert() 
DoCmd.RunSQL "insert into table TClients values('" & Parent.ContactFirstName & "','" & Parent.ContactName & "')" 
End Sub
Et presque la même chose pour la classe clFournisseur :
Code : Sélectionner tout
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
 
Option Compare Database 
Option Explicit 
 
Implements clContact 
Private Parent As clContact 
 
Private Sub Class_Initialize() 
Set Parent = New clContact 
End Sub 
 
Private Property Get clContact_ContactFirstName() As String 
Parent.ContactFirstName = Parent.ContactFirstName 
End Property 
 
Private Property Let clContact_ContactFirstName(ByVal RHS As String) 
Parent.ContactFirstName = RHS 
End Property 
 
Private Property Let clContact_ContactName(ByVal RHS As String) 
Parent.ContactName = RHS 
End Property 
 
Private Property Get clContact_ContactName() As String 
clContact_ContactName = Parent.ContactName 
End Property 
 
Private Function clContact_FullName() As String 
clContact_FullName = Parent.FullName 
End Function 
 
Private Sub clContact_Insert() 
DoCmd.RunSQL "insert into table TFournisseurs values('" & Parent.ContactFirstName & "','" & Parent.ContactName & "')" 
End Sub
Dans ces classes j'implémente la classe clContact, et donc je dois créer toutes les procédures de cette interface.
Je déclare un objet Parent de type clContact, et j'initialise cet objet à l'initialisation de la classe (Class_Initialize).
Il est alors possible d'utiliser cet objet Parent pour faire appel aux procédures et propriétés qu'on souhaite avoir dans la classe mère.

Par contre la procédure Insert est "classique".
Elle s'exécute dans le module de classe qui correspond au type de contact.

Pour tester, une petite fonction :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
Function TestPolymorphisme() 
' Deux objets "contact" 
Dim oClient1 As clContact 
Dim oFournisseur1 As clContact 
' Crée un client 
Set oClient1 = New clClient 
' Crée un fournisseur 
Set oFournisseur1 = New clFournisseur 
' Prénom et nom du client 
oClient1.ContactFirstName = "Jean" 
oClient1.ContactName = "Boulanger" 
' Prénom et nom du fournisseur 
oFournisseur1.ContactFirstName = "Pierre" 
oFournisseur1.ContactName = "Petrain" 
' Affichage des noms complets 
Debug.Print oClient1.FullName 
Debug.Print oFournisseur1.FullName 
' Insertion des contacts 
oClient1.Insert 
oFournisseur1.Insert 
End Function
La proécure FullName n'est donc écrite qu'une seule fois.
C'est utile pour une procédure qu'on ne veut pas maintenir à différents endroits à coup de copier-coller au risque d'en oublier un.

La procédure Insert est spécifique à chaque type de contact.
Bien sûr l'exemple n'est pas très pertinant, il n'y a que le nom de table qui change, mais il pourrait y avoir beaucoup plus de différences : des noms de champs différents, des vérifications de données différentes, ...

C'est pas très très joli de créer un objet Parent dans chaque objet pour lui envoyer les méthodes, mais ça marche.

Amusez-vous bien avec le polymorphisme!
Avatar de Tofalu Tofalu - Rédacteur http://www.developpez.com
le 19/11/2009 à 19:47
C'est pas très très joli de créer un objet Parent dans chaque objet pour lui envoyer les méthodes, mais ça marche.

Ca transforme surtout le sens de la notion d'héritage (sens au sens sémantique et pas coté ... ok, je sais pas m'exprimer )

C'est comme l'héritage dans une base de données relationnelle, on transforme le verbe "est un" par "a un"

Le fournisseur est un contact DEVIENT le fournisseur a une fiche contact

Mais y a pas le choix, et c'est pas vraiment handicapant
Avatar de Arkham46 Arkham46 - Responsable Access http://www.developpez.com
le 19/11/2009 à 21:14
Citation Envoyé par Tofalu  Voir le message
Le fournisseur est un contact DEVIENT le fournisseur a une fiche contact

en fait les deux :
Le fournisseur est un contact et a une fiche contact

on garde quand même le polymorphisme d'héritage (les deux objets sont déclarés as clContact), mais on rajoute une "fiche contact" pour les procédures communes
Avatar de Tonioyo Tonioyo - Membre confirmé http://www.developpez.com
le 25/11/2009 à 11:59
Je suis en train de me poser un petite question métaphysique :

Implement ne serrait pas le même concept d'interface en java ?

Un gros problème c'est qu'avec Implements en VBA on peut instancier des méthodes du coup on perds la définition d'interface.
Avatar de - http://www.developpez.com
le 01/10/2014 à 21:54
Salut,

Une utilisation du mot-clé Implements ici: [XL-2007] Module de classe et KeyPress
Offres d'emploi IT
Chef de projet SI confirmé (H/F)
Société Générale - Ile de France - Val-de-Fontenay
Ingénieur sénior en développement mobile / projet innovation H/F
Safran - Ile de France - Hauts de Seine
Software engineer H/F
Safran - Ile de France - Magny-les-Hameaux / Saclay

Voir plus d'offres Voir la carte des offres IT
Responsables bénévoles de la rubrique Excel : Pierre Fauconnier - Arkham46 -