Virtuelle / Abstraite

WLangage : Notions VIRTUELLE vs ABSTRAITE

Résumé

Mot-clé Peut être appelée telle quelle ? Redéfinition obligatoire ? Utilisation
VIRTUELLE ✅ Oui ❌ Non Permet une redéfinition optionnelle et le polymorphisme
ABSTRAITE ❌ Non ✅ Oui Force les classes filles à implémenter la méthode
Important :
Pour que le polymorphisme fonctionne correctement en WLangage, la méthode doit être déclarée avec le mot-clé VIRTUELLE dans la classe parente.
Si tu ne mets pas VIRTUELLE, la méthode de la classe parente sera appelée, même si l’objet est en réalité d’une classe dérivée !

VIRTUELLE : méthode redéfinissable

Permet de définir un comportement par défaut que les classes dérivées peuvent redéfinir si besoin.

CLASSE Animal
    PROCÉDURE VIRTUELLE Crier()
        Info("Animal fait un bruit")
FIN

CLASSE Chien HÉRITE DE Animal
    PROCÉDURE Crier()
        Info("Chien aboie")
FIN

ABSTRAITE : méthode à implémenter

Déclare une méthode sans corps. Les classes dérivées doivent obligatoirement l’implémenter.

CLASSE ABSTRAITE Animal
    PROCÉDURE ABSTRAITE Crier()
FIN

CLASSE Chien HÉRITE DE Animal
    PROCÉDURE Crier()
        Info("Chien aboie")
FIN

Analogie

  • VIRTUELLE = « Tu peux personnaliser ce comportement si tu veux. »
  • ABSTRAITE = « Tu dois me dire ce que tu fais ici, car je ne le sais pas. »

Pourquoi est-il déconseillé de lancer une méthode virtuelle dans le constructeur ?

Appeler une méthode virtuelle depuis un constructeur peut provoquer des comportements indésirables et des erreurs difficiles à débuguer. Voici pourquoi :

  • Le constructeur de la classe dérivée n’a pas encore été exécuté : Lorsque tu appelles une méthode virtuelle dans le constructeur, l’objet de la classe dérivée n’est pas encore complètement initialisé. Cela signifie que si cette méthode a été redéfinie dans une classe dérivée, tu pourrais exécuter du code dans une instance partiellement construite.
  • Les objets dérivés sont en état « partiellement construit » : L’objet est en cours de création, et certaines propriétés ou méthodes propres à la classe dérivée peuvent ne pas être accessibles ou correctement initialisées.
  • Le polymorphisme pourrait ne pas fonctionner comme prévu : Le polymorphisme dépend de la redéfinition de méthodes dans les classes dérivées. Si la méthode est appelée avant l’initialisation de la classe dérivée, c’est la version de la classe parente qui sera exécutée, ce qui peut causer des comportements inattendus.

Pour éviter ces problèmes, il est préférable d’éviter d’appeler des méthodes virtuelles depuis un constructeur. À la place, il est recommandé de les appeler après l’initialisation complète de l’objet, soit à travers une méthode distincte.

Important :
Lorsque tu appelles une méthode virtuelle depuis un constructeur, cela peut entraîner des effets imprévisibles, surtout si la classe dérivée a des méthodes ou des propriétés non encore initialisées.

Utilisation du Constructeur Privé et du Pattern « Factory »

Lorsque tu mets un constructeur en PRIVÉ, tu contrôles strictement la manière dont les objets sont créés. Cela est souvent fait dans le cadre du pattern Factory (fabrique).

Avantages d’un constructeur privé

  • Force l’utilisation d’une méthode spécifique pour créer l’objet.
  • Permet de contrôler l’initialisation de l’objet, comme l’appel de méthodes virtuelles ou d’autres logiques d’initialisation.
  • Préviens les erreurs de construction en forçant un contrôle centralisé du processus de création.
Important :
Lorsque tu utilises un constructeur privé, tu ne peux plus créer directement l’objet avec allouer. Il faut utiliser une méthode spéciale pour l’allocation.

Exemple de code

Voici un exemple de comment tu peux utiliser un constructeur privé et une méthode gNouvelleVariable() pour contrôler l’initialisation de l’objet :


CLASSE MVARChoix_LimiteDeFourniture HÉRITE DE MVARChoix

// Constructeur privé
PROCÉDURE PRIVÉE Constructeur()
    // Pas d'appel à Initialiser ici
FIN

// Méthode pour créer un objet et l'initialiser correctement
PROCÉDURE GLOBALE gNouvelleVariable() : MVARChoix_LimiteDeFourniture
    pclNouvelleVariable est un MVARChoix_LimiteDeFourniture dynamique
    pclNouvelleVariable = allouer MVARChoix_LimiteDeFourniture
    pclNouvelleVariable.Initialiser() // Appel explicite de l'initialisation
    RENVOYER pclNouvelleVariable
FIN

// Méthode privée d'initialisation de l'objet
PROCÉDURE PRIVÉE Initialiser()
    AjouterChoixPossible(FOURNITURE_INDEFINI.Valeur, "[Indéfini]", Faux)
    AjouterChoixPossible(FOURNITURE_NON_APPLICABLE.Valeur, "Non applicable", Faux)
    AjouterChoixPossible(FOURNITURE_MON_ENTITE.Valeur, GetMonEntite(), Vrai)
    AjouterChoixPossible(FOURNITURE_CLIENT.Valeur, "Fourni par le client", Faux)
    AjouterChoixPossible(FOURNITURE_EXISTANT.Valeur, "Existant/Récupéré", Faux)
FIN

// Méthode interne pour obtenir le nom de l'entité
PROCÉDURE INTERNE GetMonEntite() : chaîne
    QUAND EXCEPTION DANS
        RENVOYER {"gpclMonEntite.p_sNomComplet", indVariable}
    FAIRE
        RENVOYER "Mon entité"
    FIN
FIN
    

Dans cet exemple, nous avons une classe MVARChoix_LimiteDeFourniture avec un constructeur privé pour empêcher la création directe de l’objet. Nous avons une méthode de fabrique gNouvelleVariable() qui crée l’objet et appelle explicitement la méthode Initialiser() pour s’assurer que l’objet est correctement initialisé avant d’être utilisé.

Classe parent

  • Procédure Initialiser VIRTUELLE dans le parent.

Classe dérivée

  • Constructeur de la classe héritée en PRIVE.
  • Pas d’appel à une procédure virtuelle dans le constructeur.
  • Fabrique (Factory) pour créer l’objet qui appelle Initialiser.

Conclusion

En rendant le constructeur privé et en utilisant une méthode de fabrique, tu as un contrôle complet sur l’initialisation et la création de l’objet, ce qui est une bonne pratique pour éviter des erreurs liées à une mauvaise gestion de l’ordre des opérations.