WDForge - Forum

Le forum des développeurs professionnels WinDev ®

Poo : Création d'une collection typée

Discussion sur les ressources proposées

Message non lupar R&B » Mer 20 Jan 2016 20:56

Suite du cycle sur la programmation orientée objet, Adrien Jouanno vous propose un objet de création de collection typée.
Cette classe permet de manipuler une liste d'objet de même type, que l'objet soit directement du type voulu ou hérité quelque soit le niveau d'héritage (Liskov)....

Discutez-en ici..
R&B
R&B
Administrateur WDForge
 
Messages: 12
Enregistré le: Dim 3 Mai 2015 18:12

Message non lupar yndigos » Mar 6 Sep 2016 05:14

Bonjour M. Jouanno,

j'ai parcouru avec grand intérêt votre tutoriel ainsi que l'exemple fourni.
Oserais-je dire, pile-poil ce que je cherchais pour l'implantation que je désire mettre en place.
(programmation sous forme de entités-mapper-handler inspirée du C#.)
Je devrais ajouter aussi que mes maigres connaissances des concepts "objets" ne ma facilite pas la tâche.
J'ai donc une question relative à votre exemple. Dans ce dernier, dans la déclaration globale de la fenêtre Fen_Test, vous indiquez en commentaires :

// la collection peut se déclarer comme suit :
fo_Collection est une CL_TypableElementsCollection(allouer un Animal)
// elle tolérera les animaux mais pas les animaux spécifiques (chat, chien, poule)
// il faudra donc, si on souhaite aggrandir la tolérance, le spécifier par appel aux boutons "tolérer les ..."


J'avoue ne pas comprendre cette idée d'aggrandir la tolérance via les boutons "tolérer les...".
Qu'est-ce que cela signifie concrètement, comment les mettre en place, qu'est-ce que cela implique comme limitations, etc...

Merci pour votre retour,

Laurent Richelle
yndigos
Stagiaire WDF
 
Messages: 3
Enregistré le: Mar 6 Sep 2016 05:04

Message non lupar yndigos » Mar 6 Sep 2016 08:32

Toujours plein d'interrogations de mon côté.
Sur base de l'exemple fourni, f'essaye d'imaginer comment je pourrais ajouter dans une classe parent (animal) une méthode générique pour alimenter des combos en fonction du type d'objet manipulé (ex : une combo avec uniquement les chats)
J'imagine que cela est possible, mais...

Une piste ou une simple référence me fairait plaisir.

Yndigos (aka Laurent Richelle)
yndigos
Stagiaire WDF
 
Messages: 3
Enregistré le: Mar 6 Sep 2016 05:04

Message non lupar gwennhadu » Jeu 29 Sep 2016 14:49

Bonjour Yndigos,

Tout d'abord mes excuses pour cette réponse tardive.

yndigos a écrit:J'ai donc une question relative à votre exemple. Dans ce dernier, dans la déclaration globale de la fenêtre Fen_Test, vous indiquez en commentaires :

// la collection peut se déclarer comme suit :
fo_Collection est une CL_TypableElementsCollection(allouer un Animal)
// elle tolérera les animaux mais pas les animaux spécifiques (chat, chien, poule)
// il faudra donc, si on souhaite aggrandir la tolérance, le spécifier par appel aux boutons "tolérer les ..."


J'avoue ne pas comprendre cette idée d'aggrandir la tolérance via les boutons "tolérer les...".
Qu'est-ce que cela signifie concrètement, comment les mettre en place, qu'est-ce que cela implique comme limitations, etc...


Il s'agit d'une erreur de ma part, issue d'un exemple qui ne bénéficiait pas du contrôleur de type. Dans cet ancien exemple, j'ai été contraint de mettre un "limiteur" qui empêchait d'ajouter autre chose qu'un objet du type exact que celui qui a servi à instancier la collection (donc ici un animal), il fallait rajouter via les boutons "tolérer ..." les types supplémentaires (chats, chiens,...). Pas de polymorphisme à l'époque :x

Maintenant le contrôleur de type peut de lui-même vérifier si l'objet fourni est bien du type attendu (chat est aussi un animal, donc accepté :) ).

Merci en tout cas, je vais corriger ça.

yndigos a écrit:Sur base de l'exemple fourni, f'essaye d'imaginer comment je pourrais ajouter dans une classe parent (animal) une méthode générique pour alimenter des combos en fonction du type d'objet manipulé (ex : une combo avec uniquement les chats)


Une méthode générique traitant des animaux "spéciaux" (chat, chien ,...) serait en toute logique du ressort de l'animal comme vous le suggérez, mais en conception orientée objet ce n'est pas le cas : un "animal" n'a pas de connaissance de l'existence de "chat", c'est "chat" qui a connaissance de l'existence de "animal".

La collection générique n'est pas non plus une bonne piste, car elle n'est pas censée fournir des traitements ensemblistes sur ses éléments : son rôle est de les stocker sous conditions et de les restituer.
Une bonne solution consisterait à créer une classe ou une collection de procédures "Traitements d'animaux", à laquelle vous fournissez une collection typée qu'elle va parcourir afin d'effectuer les traitements voulus (alimenter les combos en fonction du type).

Pour ce qui est de déterminer le type d'un élément, je vous invite à regarder le code du bouton "Bt_Oeufs" de "Fen_Test" :
Code: Tout sélectionner
lpo_Animal est un Animal dynamique
ldef_Poule est une Définition = RécupèreDéfinition(allouer une Poule())
SI fo_Collection:Courant(lpo_Animal) ALORS
   SI PAS lpo_Animal..Classe = ldef_Poule..Nom ALORS      
      Info(ChaîneConstruit("Ce n'est pas sûr qu'un %1 ponde des oeufs...",lpo_Animal..Classe))
   SINON
      lpo_Poule est une Poule dynamique <- lpo_Animal
      SI lpo_Poule:EstPondeuse() ALORS
         Info("oui... plop!")
      SINON
         Info("non")
      FIN
   FIN
FIN


On trouve ici un test permettant de résoudre le type de l'objet récupéré (c'est une poule ou bien ...?) :
Code: Tout sélectionner
SI PAS lpo_Animal..Classe = ldef_Poule..Nom ALORS


Vous pouvez appliquer ce test à un parcours de la collection, en utilisant un
Code: Tout sélectionner
SELON lpo_Animal..Classe
   CAS ldef_Poule..Nom : // c'est une poule
   CAS ldef_Chat..Nom : // c'est un chat
FIN

par exemple pour décider du traitement à appliquer.

Autre solution, créer une instance du contrôleur de type afin de déterminer si un objet est du type demandé ou un dérivé. Toutes les infos ici : http://www.wdforge.org/index.php/a-g-l/wdforge/28-news-guides/64-poo-sequences-d-objets-structurants

Bon dev !
gwennhadu
Stagiaire WDF
 
Messages: 7
Enregistré le: Mar 30 Juin 2015 12:13

Message non lupar R&B » Mer 12 Oct 2016 17:55

Information
Le projet a été corrigé et mis à jour.
Télécharger
R&B
R&B
Administrateur WDForge
 
Messages: 12
Enregistré le: Dim 3 Mai 2015 18:12

Message non lupar yndigos » Ven 11 Nov 2016 10:13

Bonjour R_B (ou gwennhadu ?)

Je n'ai pas répondu plutôt, mais merci pour ces informations.

Malgré vos explications, je buttes encore sur des aspects "pratiques".
Je m'explique :

Dans une application, je désire utiliser un dictionnaire multilingue. Ce dictionnaire comporte un ensemble d'enregistrement, et chaque enrgistrement se compose entre autre d'un identifiant et de traductions dans les différentes langues manipulées dans l'application.

Benoîtement, je me suis dit : cool, nous allons créer un objet ENTITE basée sur l'enregistrement du dictionnaire et créer une liste typée de la susdite entité (lstDictionnaire) sur base de la liste typée.
Super, j'y suis arrivé, je manipule mes enregistrements, je peux même la charger au début du projet et ainsi, j'accède très rapidement à mes messages (warning, errors,...) , mes libellés etc sans devoir jouer du va et vient avec ma db.
Cependant, la classe CL_TypableElementsCollection est finalement très générique et pour l'usage d'un dictionnaire, je pourrais y ajouter quelques fonctionnalités supplémentaires. Allons-y pour une classe enfant qui hérite directement de notre classe CL_TypableElementsCollection. Et c'est là que cela part en sucette...

Mon "problème" pourrait se résumer de la manière suivante :
Comment assigner (est-ce le bon terme) ou copier le contenu d'une liste typée "générique" à ou dans mon objet enfant liste dictionnaire (lstDictionnaire).
Formulé autrement, j'imagine que cela revient à assigner à l'ancêtre de mon dictionnaire un objet du même type que mon ancêtre.

J'ai le sentiment qu'en C#, cette manipulation est très facile grâce au mécanisme de casting. Avec windev, je ne vois absolument pas comment m'y prendre...

Votre expérience semblant être bien plus avancée que moi sur le sujet, votre point de vue me serait dès lors fort fort utile.

Cordialement,
Laurent Richelle
yndigos
Stagiaire WDF
 
Messages: 3
Enregistré le: Mar 6 Sep 2016 05:04

Message non lupar gwennhadu » Sam 12 Nov 2016 14:11

Bonjour yndigos,
Comment assigner (est-ce le bon terme) ou copier le contenu d'une liste typée "générique" à ou dans mon objet enfant liste dictionnaire (lstDictionnaire).

Je ne comprends pas le problème, pour la raison suivante : la liste typée du projet doit être "typée" (on choisit une classe de base) dès son instanciation (c'est le paramètre du constructeur qui donne le type). Une liste "générique" ne peut pas exister en exécution.

Je vais reprendre la définition de votre dictionnaire, afin d'éclaircir le terrain.

Je me permets un parallèle avec C#, la déclaration en windev :
Code: Tout sélectionner
lstDictionnaire est une CL_TypableElementCollection(allouer un ENTITE)

correspond (en très très gros ;) ) à :
List<ENTITE> lstDictionnaire = new List<ENTITE>();

L'objet Windev lstDictionnaire est bien une liste d'ENTITE, pouvant aussi accepter les dérivés d'ENTITE.
Maintenant, fonctionnellement parlant la liste typée seule ne suffit pas, vous voulez des fonctionnalités supplémentaires propres à un dictionnaire. On peut y arriver de deux façons : héritage simple, ou bien composition (au sens UML).

Partons pour l'héritage simple, écrivons le code de la classe Dictionnaire (attention j'ai pas testé) :
Code: Tout sélectionner
Dictionnaire est une classe
   hérite de CL_TypableElementCollection // héritage simple, PUBLIC par défaut
FIN

Procedure Constructeur()
   CL_TypableElementCollection:Constructeur(allouer une ENTITE) // appel du constructeur ancêtre, je privilégie cette syntaxe

// et après j'ajoute / je surcharge les fonctions qui m'intéressent...
// pour exemple :
Procedure TousLesElementsCommencantParA() : chaine

lc_Resultat est une chaine
lpo_Entite est une ENTITE Dynamique
:Premier()
TANTQUE :Courant(lpo_Entite)
   SI lpo_Entite:CommencePar("A") ALORS // trop forte cette Entite...
      lc_Resultat += lpo_Entite:Contenu()
   FIN
   :Suivant()
FIN

RENVOYER lc_Resultat

Là on a un dictionnaire qui est une liste typée verrouillée sur le type de base : ENTITE, avec des fonctions de dictionnaire qui travaillent sur ses propres éléments (du Dictionnaire).
Dictionnaire gère son contenu comme un CL_TypableElementCollection.

La composition, que je privilégie (principe d'encapsulation, pas de hiérarchie de classes en cascades, plus simple à maintenir)
Code: Tout sélectionner
Dictionnaire est une classe
   PRIVE // de cette manière on considère que dictionnaire est pleinement responsable de son contenu (ce qui est le mieux du point de vue POO, dans le cas contraire utiliser le mot-clé PUBLIC)
   lstEntites est une CL_TypableElementCollection(allouer une ENTITE) // 'lstEntites' à la place de 'lstDictionnaire' pour une meilleure sémantique
FIN

// et après j'ajoute les fonctions qui m'intéressent... et qui vont travailler sur mon membre lstEntites.
// pour exemple :
Procedure TousLesElementsCommencantParA() : chaine

lc_Resultat est une chaine
lpo_Entite est une ENTITE Dynamique
:lstEntites:Premier()
TANTQUE :lstEntites:Courant(lpo_Entite)
   SI lpo_Entite:CommencePar("A") ALORS // trop forte cette Entite...
      lc_Resultat += lpo_Entite:Contenu()
   FIN
   :lstEntites:Suivant()
FIN

Là on a un dictionnaire qui a une liste typée verrouillée sur le type de base : ENTITE, avec des fonctions de dictionnaire qui travaillent sur les éléments de la liste typée (du Dictionnaire).
Dictionnaire délègue son contenu à un membre de type CL_TypableElementCollection.

Avec une des deux méthodes vous disposez maintenant d'un dictionnaire pleinement fonctionnel.

J’espère avoir pu répondre à votre problème.
Sinon, avez toujours besoin de copier des éléments d'une liste à une autre ? si c'est le cas je referais un autre post.

En vous souhaitant un bon développement.
gwennhadu
Stagiaire WDF
 
Messages: 7
Enregistré le: Mar 30 Juin 2015 12:13


Retourner vers Ressources

cron