web-dev-qa-db-fra.com

Quand utiliser une classe dans VBA?

Quand est-il approprié d'utiliser une classe dans Visual Basic pour Applications (VBA)?

Je suppose que le développement accéléré et la réduction de l'introduction de bogues est un avantage commun pour la plupart des langues prenant en charge la POO. Mais avec VBA, existe-t-il un critère spécifique? 

37
Curtis Inderwiesche

Cela dépend de qui va développer et maintenir le code. Les auteurs de macro typiques "Utilisateurs intensifs" qui piratent de petites applications ad-hoc peuvent bien être confondus en utilisant des classes. Mais pour un développement sérieux, les raisons d'utiliser des classes sont les mêmes que dans d'autres langues. Vous avez les mêmes restrictions que VB6 - pas d'héritage - mais vous pouvez avoir un polymorphisme en utilisant des interfaces.

Une bonne utilisation des classes consiste à représenter des entités et des ensembles d'entités. Par exemple, je vois souvent du code VBA qui copie une plage Excel dans un tableau à deux dimensions, puis manipule le tableau à deux dimensions avec un code comme:

Total = 0
For i = 0 To NumRows-1
    Total = Total + (OrderArray(i,1) * OrderArray(i,3))
Next i

Il est plus facile de copier la plage dans une collection d'objets avec des propriétés nommées correctement, quelque chose comme:

Total = 0
For Each objOrder in colOrders
    Total = Total + objOrder.Quantity * objOrder.Price
Next i

Un autre exemple consiste à utiliser des classes pour implémenter le modèle de conception RAII (Google pour celui-ci). Par exemple, une chose que je devrais faire est de déprotéger une feuille de travail, de faire quelques manipulations, puis de la protéger à nouveau. L'utilisation d'une classe garantit que la feuille de calcul sera toujours protégée à nouveau, même en cas d'erreur dans votre code:

--- WorksheetProtector class module ---

Private m_objWorksheet As Worksheet
Private m_sPassword As String

Public Sub Unprotect(Worksheet As Worksheet, Password As String)
    ' Nothing to do if we didn't define a password for the worksheet
    If Len(Password) = 0 Then Exit Sub

    ' If the worksheet is already unprotected, nothing to do
    If Not Worksheet.ProtectContents Then Exit Sub

    ' Unprotect the worksheet
    Worksheet.Unprotect Password

    ' Remember the worksheet and password so we can protect again
    Set m_objWorksheet = Worksheet
    m_sPassword = Password
End Sub

Public Sub Protect()
    ' Protects the worksheet with the same password used to unprotect it
    If m_objWorksheet Is Nothing Then Exit Sub
    If Len(m_sPassword) = 0 Then Exit Sub

    ' If the worksheet is already protected, nothing to do
    If m_objWorksheet.ProtectContents Then Exit Sub

    m_objWorksheet.Protect m_sPassword
    Set m_objWorksheet = Nothing
    m_sPassword = ""
End Sub

Private Sub Class_Terminate()
    ' Reprotect the worksheet when this object goes out of scope
    On Error Resume Next
    Protect
End Sub

Vous pouvez ensuite l'utiliser pour simplifier votre code:

Public Sub DoSomething()
   Dim objWorksheetProtector as WorksheetProtector
   Set objWorksheetProtector = New WorksheetProtector
   objWorksheetProtector.Unprotect myWorksheet, myPassword

   ... manipulate myWorksheet - may raise an error

End Sub 

Une fois ce sous-objet terminé, objWorksheetProtector est hors de portée et la feuille de calcul est à nouveau protégée.

50
Joe

Je pense que les critères sont les mêmes que ceux des autres langues

Si vous avez besoin de lier plusieurs données et méthodes, ainsi que de gérer spécifiquement ce qui se passe lorsque l'objet est créé/terminé, les classes sont idéales

si vous avez quelques procédures qui se déclenchent lorsque vous ouvrez un formulaire et que l’une d’elles prend beaucoup de temps, vous pouvez décider de chronométrer chaque étape ...... 

Vous pouvez créer une classe de chronomètre avec des méthodes pour les fonctions évidentes de démarrage et d'arrêt, puis ajouter une fonction permettant de récupérer l'heure et de la signaler dans un fichier texte, à l'aide d'un argument représentant le nom du processus en cours de chronométrage. Vous pouvez écrire une logique pour consigner uniquement les performances les plus lentes à des fins d’investigation. 

Vous pouvez ensuite ajouter un objet de barre de progression avec des méthodes pour l'ouvrir et le fermer et afficher les noms de l'action en cours, ainsi que les temps en ms et le temps restant probablement basé sur les rapports stockés précédents, etc.

Un autre exemple pourrait être que si vous n'aimiez pas les déchets du groupe d'utilisateurs d'Access, vous pouvez créer votre propre classe User avec des méthodes de connexion et de déconnexion et des fonctionnalités pour le contrôle/l'audit d'accès au niveau du groupe/la consignation de certaines actions/erreurs de suivi, etc. 

Bien sûr, vous pouvez le faire en utilisant un ensemble de méthodes sans rapport et beaucoup de variables, mais avoir tout encapsulé dans une classe me semble simplement préférable. 

Tôt ou tard, vous vous approchez des limites de VBA, mais son langage est assez puissant et si votre entreprise vous y lie, vous pouvez réellement en tirer de bonnes solutions complexes. 

8
ajp

Les classes sont extrêmement utiles pour traiter les fonctions plus complexes de l'API, en particulier lorsqu'elles nécessitent une structure de données.

Par exemple, les fonctions GetOpenFileName () et GetSaveFileName () prennent une structure OPENFILENAME avec plusieurs membres. vous n’aurez peut-être pas besoin de tous les exploiter, mais ils sont là et doivent être initialisés.

J'aime envelopper la structure (UDT) et les déclarations de fonction API dans une classe CfileDialog. L'événement Class_Initialize définit les valeurs par défaut des membres de la structure. Ainsi, lorsque j'utilise la classe, je ne dois définir que les membres à modifier (via les procédures Property). Les constantes d'indicateur sont implémentées sous forme d'énumération. Ainsi, par exemple, pour choisir une feuille de calcul à ouvrir, mon code pourrait ressembler à ceci:

Dim strFileName As String
Dim dlgXLS As New CFileDialog

With dlgXLS
  .Title = "Choose a Spreadsheet"
  .Filter = "Excel (*.xls)|*.xls|All Files (*.*)|*.*"
  .Flags = ofnFileMustExist OR ofnExplorer

  If OpenFileDialog() Then
    strFileName = .FileName
  End If
End With
Set dlgXLS = Nothing

La classe définit le répertoire par défaut sur Mes documents, bien que je puisse le changer avec la propriété InitDir si je le voulais. 

Ceci n'est qu'un exemple de la façon dont une classe peut être extrêmement bénéfique dans une application VBA.

5
Mike

Je ne dirais pas qu'il existe un critère spécifique, mais je n'ai jamais vraiment trouvé d'endroit utile pour utiliser Classes en code VBA. Dans mon esprit, cela est tellement lié aux modèles existants autour des applications Office que l'ajout d'une abstraction supplémentaire en dehors de ce modèle d'objet ne fait que créer de la confusion.

Cela ne veut pas dire qu'un ne pourrait pas trouver une place utile pour un cours dans VBA, ou faire des choses parfaitement utiles en utilisant un cours, juste que je ne les ai jamais trouvés utiles dans cet environnement.

5
cori

Vous pouvez également réutiliser le code VBA sans utiliser les classes réelles. Par exemple, si vous avez un appelé, VBACode. Vous pouvez accéder à n’importe quelle fonction ou sous-fonction de n’importe quel module avec la syntaxe suivante:

VBCode.mysub(param1, param2)

Si vous créez une référence à un modèle/doc (comme une dll), vous pouvez référencer le code d'autres projets de la même manière.

3
JonnyGold

Développer des logiciels, même avec Microsoft Access, en utilisant la programmation orientée objet est généralement une bonne pratique. Cela permettra une évolutivité future en permettant aux objets d'être couplés de manière lâche, avec de nombreux avantages. Cela signifie essentiellement que les objets de votre système seront moins dépendants les uns des autres. Le refactoring devient donc beaucoup plus facile. Vous pouvez y parvenir grâce à l'accès via Class Modules. L'inconvénient est que vous ne pouvez pas effectuer l'héritage de classe ou le polymorphisme dans VBA. En fin de compte, il n'y a pas de règle absolue sur l'utilisation de classes, mais simplement sur les meilleures pratiques. Mais gardez à l'esprit que plus votre application grandit, plus il est facile de maintenir l'utilisation des classes. 

2
jinsungy

Pour la récursivité des données (gestion des nomenclatures, par exemple), une classe personnalisée est extrêmement utile et, parfois, indispensable. Vous pouvez créer une fonction récursive sans module de classe, mais de nombreux problèmes de données ne peuvent pas être résolus efficacement. 

(Je ne sais pas pourquoi les gens ne vendent pas des ensembles de bibliothèques de nomenclature pour VBA. Peut-être que les outils XML ont fait la différence.) 

Les instances de formulaire multiples sont l’application courante d’une classe (de nombreux problèmes d’automatisation sont insolubles), je suppose que la question concerne les classes custom

2
Smandoli

J'utilise des classes si je veux créer un paquet de code auto-encapsulé que je vais utiliser dans de nombreux projets VBA rencontrés par différents clients.

2
Jason TEPOORTEN

J'utilise des classes lorsque j'ai besoin de faire quelque chose et une classe le fera mieux :) Par exemple, si vous devez répondre à des événements (ou les intercepter), vous avez besoin d'un cours. Certaines personnes détestent les UDT (types définis par l'utilisateur) mais je les aime bien, donc je les utilise si je veux du code auto-documenté en anglais clair. Pharmacy.NCPDP étant beaucoup plus facile à lire que strPhrmNum :) Mais un UDT est limité, alors disons que je veux pouvoir définir Pharmacy.NCPDP et que toutes les autres propriétés soient remplies. Et je veux aussi le faire pour que vous ne puissiez pas modifier accidentellement les données. Ensuite, j'ai besoin d'une classe, car vous n'avez pas de propriétés en lecture seule dans un UDT, etc. 

Une autre considération est simplement la lisibilité simple. Si vous utilisez des structures de données complexes, il est souvent utile de savoir que vous devez simplement appeler Company.Owner.Phone.AreaCode pour essayer de savoir où tout est structuré. Surtout pour les personnes qui doivent maintenir cette base de code 2 ans après votre départ :)

Mes deux cents est "Code With Purpose". Ne pas utiliser une classe sans raison. Mais si vous avez une raison, faites-le :)

2
Oorang

Vous pouvez définir une classe d’encapsuleur SQL plus accessible que les jeux d’enregistrements et querydefs. Par exemple, si vous souhaitez mettre à jour une table en fonction d'un critère dans une autre table liée, vous ne pouvez pas utiliser de jointure. Vous pouvez construire un recorset vba et querydef pour le faire, mais je le trouve plus facile avec une classe. En outre, votre application peut avoir un concept qui nécessite plus de 2 tables, il serait peut-être préférable d'utiliser des classes pour cela. Par exemple. Vous application suivre les incidents. Incident possède plusieurs attributs qui seront conservés dans plusieurs tables {utilisateurs et leurs contacts ou profils, description de l'incident; suivi de l'état; Des listes de contrôle pour aider l'agent d'assistance à répondre à l'incident; Répondre ...} . Pour garder une trace de toutes les requêtes et relations impliquées, oop peut être utile. C'est un soulagement de pouvoir utiliser Incident.Update (xxx) au lieu de tout le codage ...

0
Bellow Akambi