Modifier

Introduction

Vous avez été nombreux à apprécier le premier article sur la POO. Et bien bravo, vous m'avez motivé à écrire un deuxième article (je sais, on attend toujours le deuxième article sur les expressions régulières, mais je n'ai pas encore trouvé l'inspiration).

Dans cet article, je vais vous présenter un exemple où je compare la POO à la programmation procédurale. Vous pouvez télécharger cet exemple sur le dépôt PCSoft, mais s'il vous plaît, pour ceux qui ne connaissent pas la POO, attendez que l'article vous invite à examiner le code pour ne pas aller trop vite.

En effet, je veux continuer à vous apprendre le pourquoi (l'encapsulation et le polymorphisme) et j'ai peur que vous ne vous arrêtiez qu'au comment (création de classe, méthode, propriété, etc...). C'est une erreur que j'ai faîte quand j'ai commencé à apprendre et je me suis mis à vouloir mettre de l'objet partout, sans réfléchir. Pire, j'ai entraîné mes collègues dedans et le code résultant (toujours en activité) me déçoit énormément aujourd'hui.

Prenez le temps...

Télécharger l'exemple

La POO par l'exemple

Avant toute chose, il faut savoir que tout ce que l'on peut faire en procédural, on peut le faire en POO. Mais l'inverse est également vrai. La POO n'est pas mieux que le procédural, la POO c'est différent.

Dans ma manière de vous la présenter, je vais faire en sorte de vous faire préférer la POO, puisque je veux que vous compreniez les avantages. Le niveau de qualité du premier exemple (programmation procédural) est inférieur à celui du second (POO). C'est fait exprès.

En procédural

Le premier exemple est écrit en procédural. Il permet de manipuler une table, une liste et une zone répétée. Trois actions sont possibles sur chacun des champs :

  • Ajouter un élément
  • Supprimer l'élément sélectionné
  • Afficher le contenu dans une liste

Je vous laisse prendre connaissance du code de la fenêtre fenTest01 (pour les novices, Ctrl + E puis tapez 01 et faîtes Entrée). Le code qui nous intéresse se trouve dans les 3 boutons Ajouter un élément, Supprimer l'élément sélectionné et Tracer le contenu.

Vous constaterez que pour chaque champ, on utilise les méthodes avec le préfixe qui va : Tablexxx, Listexxx et ZoneRépétéexxx.

J'aurai pu factoriser un peu le code, mais volontairement, je ne l'ai pas fait, parce que tout le monde sait que la POO, c'est mieux ! (j'espère que vous aurez perçu l'ironie).

Enfin, j'attire votre attention sur la présence d'un mot clé particulier, c'est le mot clé SELON. On verra plus tard pourquoi j'insiste dessus , mais il a son importance.

Voilà, vous avez testé la fenêtre et vous avez fait le tour. Vous en maîtrisez le code, c'est parfait.

Avec la POO

Maintenant, je vous laisse prendre connaissance du code de la fenêtre fenTest02 (pour les novices, Ctrl + E, 02, etc...).

C'est la même fenêtre et elle fait la même chose. Testez-là, vous ne verrez pas de différence.

Le code qui nous intéresse se trouve toujours dans les 3 boutons. Voici le code du bouton Ajouter un élément :

nouveau_libellé est une chaîne = DonneGUID(guidFormaté)

champ est un cChampAvecElements dynamique = recupérer_champ()
champ.ajouter(nouveau_libellé)

Et comparons le avec le code précédent :

nouveau_libellé est une chaîne = DonneGUID(guidFormaté)

SELON brQuelChamp
    // La table
    CAS 1 : TableAjoute(taElements, nouveau_libellé)

    // La liste
    CAS 2 : ListeAjoute(lsElements, nouveau_libellé)

    // La zone répétée
    CAS 3 : ZoneRépétéeAjoute(zrElements, nouveau_libellé)
FIN

Et là, je veux attirer votre attention sur les deux notions dont je vous parle sans arrêt :

L'encapsulation

La première, c'est l'encapsulation.

Je ne sais pas comment fonctionne la méthode Ajouter car j'ai masqué le détail de l'implémentation dans la classe. J'ai donc défini ce que l'on appelle un niveau d'abstraction.

Je n'ai pas à savoir si je dois utiliser TableAjoute, ListeAjoute ou ZoneRépétéeAjoute qui sont des fonctions très précises. Dans tous les cas, j'aurai simplement à utiliser la méthode Ajouter qui est beaucoup plus abstraite. La POO permet de simplifier les choses en les rendant plus abstraites.

Le polymorphisme

La seconde, c'est le polymorphisme.

Je travaille avec un objet de type cChampAvecElements. Ouvrez la fonction récupérer_champ (Pour les novices, mettez le curseur dessus puis touche F2). Vous retrouvez le code suivant :

PROCEDURE recupérer_champ()
champ est un cChampAvecElements dynamique
SELON brQuelChamp
    // La table
    CAS 1 : champ = vl.champ_table

    // La liste
    CAS 2 : champ = vl.champ_liste

    // La zone répétée
    CAS 3 : champ = vl.champ_zone_répétée            
FIN
RENVOYER champ

On constate les choses suivantes :

On retrouve le SELON. L'objectif du polymorphisme est de faire disparaître les SELON. Si vous utilisez un SELON, alors il y a de forte chance que vous pouvez utiliser le polymorphisme à la place, sauf si vous êtes dans ce que l'on appelle une fabrique (factory en anglais). Ce qui est un peu le cas dans notre exemple (pas tout à fait car on ne crée pas les objets dans cette fonction mais on va faire comme si...).

On voit que l'on a trois variables locales (si le vl. ne vous dit rien du tout, je vous invite à lire l'article WinDev - Convention de codage dans mes projets).

Elles sont définies dans la partie déclaration de la fenêtre :

PROCEDURE MaFenêtre()
stVariableFenêtre est une Structure
    champ_table est un cChampAvecElements dynamique
    champ_liste est un cChampAvecElements dynamique
    champ_zone_répétée est un cChampAvecElements dynamique
FIN

vl est une stVariableFenêtre

LOCAL
    vl.champ_table = allouer un cChampTable(taElements, taElements.Colonne1)    
    vl.champ_liste = allouer un cChampListe(lsElements) 
    vl.champ_zone_répétée = allouer un cChampZoneRépétée(zrElements, zrElements.coLibellé)

Chaque variable a un type différent (cChampTable, cChampListe et cChampZoneRépétée) mais on peut les stocker dans une variable de type cChampAvecElements. C'est cela qu'on appelle le polymorphisme. Un objet cChampAvecElements peut avoir plusieurs formes.

Conclusion sur l'exemple

Avec le polymorphisme et l'encapsulation, on essaie au maximum de masquer les détails pour permettre au développeur de comprendre beaucoup plus facilement l'intention réelle du code et de permettre une évolution du code.

En effet, si j'ai besoin d'utiliser un champ de saisie, je n'ai qu'à créer une classe cChampSaisie et qui fonctionnera de la même manière que les autres. Je n'aurai qu'à modifier la fonction récupérer_champ pour que mon code continue à fonctionner.

Enfin, je vous invite à lire les codes des deux autres boutons. Le bouton Tracer le contenu est celui que je trouve le plus intéressant.

Conclusion

Dans cet article, je suis encore resté sur le pourquoi. J'espère que cet exemple vous a permis de mieux comprendre les deux notions que sont l'encapsulation et le polymorphisme.

Si vous le souhaitez, vous pouvez commencer à explorer le comment en étudiant les classes cChampAvecElements, cChampTable, cChampListe et cChampZoneRépétée. Je les expliquerai en détail dans un prochain article.

Si vous souhaitez aller plus loin, je vous invite à étudier d'autres projets qui utilisent la POO. Par exemple, vous pouvez vous tourner vers mes projets yLogger et yImageComposée (à paraître bientôt) qui sont de parfaits exemples (j'y mélange allègrement de la POO et de la programmation procédurale).

Enfin, j'espère que cet article vous a plu et vous a appris des choses. N'hésitez pas à me donner votre avis par commentaire. J'accepte tout (questions, remarques, conseils, critiques, etc...).

Et si vous souhaitez partager cet article, n'hésitez pas, ça me fera très plaisir.

Je vous souhaite à tous une belle journée.

Article suivant Article précédent

Blog Comments powered by Disqus.