IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Problemes avec implementation de decorateurs


Sujet :

C++

  1. #1
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut Problemes avec implementation de decorateurs
    Salut les gars,

    J'ai quelques soucis pour implementer des decorateurs dans un projet C++.

    J'ai voulu prendre comme modele celui qui se trouve dans la section design patterns de developpez.com.

    En fait mon souci c'est que je peux creer mondecorateur a partir de une classe de base grace a un new, mais lors de l'appel des fonctions specifiques de chaque decorateur je trouve une erreur de compilation... la classe apres application du decorateur est toujours considere comme classe de base (DestructibleElement).

    Le code ou je me sers des decorateurs est le suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
     
    #include <iostream>
    #include <cstdlib>
    #include <ctime>
     
    #include "DecoratorWarElement.h"
    #include "DecoratorArcher.h"
    #include "DecoratorColor.h"
     
    time_t aStart, aEnd; ///@@@ time performance
     
    using namespace std;
     
    int main(int argc, char *argv[])
    {
    	srand (time(NULL));
     
    /*
        BaseAliment *c=new Aliment(1.5,"Gauffre");
        cout<< (*c)<<endl;
        c=new DecorateurGout(*c,0.2,"Sucre");
        cout<<*c<<endl;
        c=new DecorateurGout(*c,0.3,"Nutella");
        cout<<*c<<endl;
    */
     
     
    	std::cout<<"Hello World !!"<<std::endl;
     
    	DestructibleElement * aDestructibleElement1 = new DestructibleElement(100); // Points of life 100 
     
    	aDestructibleElement1 = new DecoratorWarElement(*aDestructibleElement1,200, 60); //Points of life upgrade 200 / Power 60
     
    	std::cout<<"Points of life of Element1 before first attack -> " <<aDestructibleElement1->getPointsOfLife()<<std::endl;
    	std::cout<<"Power of Element1 before first attack -> " <<aDestructibleElement1->getPower()<<std::endl;
     
    	aDestructibleElement1 = new DecoratorColor(*aDestructibleElement1, "Pink");
    //	std::cout<<"Color of Element1 before first attack -> " <<aDestructibleElement1->getColor()<<std::endl;
    	std::cout<<"Points of life of colored Element1 before first attack -> " <<aDestructibleElement1->getPointsOfLife()<<std::endl;
     
    /*
    	aDestructibleElement2 = new DecoratorArcher(*aDestructibleElement2, 200, 60); //Points of life 200 / Power 60
     
    	//Shows aDestructibleElement2 points of life
    	std::cout<< "Points of life of Element2 before first attack -> "<<aDestructibleElement2->getPointsOfLife()<<std::endl;
     
    	//aDestructibleElement1 attacks aDestructibleElement2
    	aDestructibleElement1->attack(aDestructibleElement2);	
    	std::cout<< "Points of life of Element2 after first attack -> "<<aDestructibleElement2->getPointsOfLife()<<std::endl;
    */	
    	//freeing the allocated memory
    	delete aDestructibleElement1;
    //	delete aDestructibleElement2;
     
     
      return EXIT_SUCCESS;
    }
    Pouvez vous m'eclaircir un peu svp ?

    Je mets en piece jointe les fichiers de l'implementation des decorateurs.

    Merci bcp !

  2. #2
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Salut,

    Citation Envoyé par donkeyquote Voir le message
    En fait mon souci c'est que je peux creer mondecorateur a partir de une classe de base grace a un new, mais lors de l'appel des fonctions specifiques de chaque decorateur je trouve une erreur de compilation... la classe apres application du decorateur est toujours considere comme classe de base (DestructibleElement).
    Tes explications sont loin d'être claires...
    Essaye de dire très exactement quelle ligne de code produit quelle erreur de compilation peut-être ?
    (mets un code minimal aussi parce qu'avoir toutes ces lignes en commentaires et ce rar avec tous ces fichiers dans plein de répertoires n'aident pas beaucoup)

    Bon j'ai quand même réussi à voir que y'a une fuite mémoire puisque le décorateur devient visiblement possesseur du décoré mais il ne le détruit pas (oui j'ai téléchargé le rar et j'ai fini par trouver le bon fichier).

    MAT.

  3. #3
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Desole si je n'ai pas ete assez explicite ...
    En fait dans les fichiers que je vous ai joints il y a 4 parties :

    1) Un "element" qui s'appele "Destructible Element" et qui se trouve dans la premiere couche du repertoire Element (include pour les .h et src pour les .cpp). C'est l'element de base qui va etre "decore par les decorateurs". Dans un premier moment je l'avais declare classe virtuelle pure, mais je me suis rendu compte que cela n'etait pas vraiement possible (ou je ne sais pas trop comment faire, peut-etre passer par une classe intermediaire, mais a quoi les decorateurs serviraient alors ?) si en suite je voulais faire une instanciation de cette objet afin de le decorer...

    2) Un repertoire avec des decorateurs (Decorator) DecoratorWarElement, DecoratorArcher, DecoratorColor (celui-ci est un dummy). C'est le DecoratorWar que j'ai utilise pour faire mes premiers tests... (qui ne marchent pas )

    3)Une factory qui prends en chage la creation des elements... elle n'est pas vraiment au point car je l'ai repris d'une ancienne version qui n'outilise pas de decorateurs... donc des idees d'adaptation a mon cas sont les bien venues

    4)Dans le repertoire "test" a l'interieur du repertoire Decorator il y a le code qui fait les test des decorateurs (celui qui donne l'erreur de compilation).

    Le but de mes decorateur est de faire en sorte de rajouter de surcouches a l'element de base afin de pouvoir creer des elements de propietes composees... par example avec un decorateur canon, WarElement et un autre tour, pouvoir reusir a avoir une tour-canon a partir d'un element de base "DestructibleElement"

    L'erreur que je trouve est el suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    src/TestDecorator.cpp:46: error: ‘class DestructibleElement’ has no member named ‘getPower’
    Dans le cas de l'appel de GetPointsofLife il n'y a pas de souci car la methode est virtuelle existant dans la classe de base ... mais je pense que le but du decorateur est de faire independant les methodes du decorateurs de celles declarees dans la methode de base, je me trompe ?

    Je ne sais pas si j'ai ete suffissament explicite cette fois-ci, n'hesitez pas a me demander plus d'explications car la resolution de ce cas serait vraiment interessant pour moi d'un point de vue pedagogique.

    Merci encore.

  4. #4
    Membre du Club
    Inscrit en
    Mai 2005
    Messages
    73
    Détails du profil
    Informations forums :
    Inscription : Mai 2005
    Messages : 73
    Points : 68
    Points
    68
    Par défaut
    Je pense que tu as mal compris le polymorphisme : en utilisant l'héritage, tu permets de spécifier que DestructibleElement et DecoratorWarElement peuvent être traités de la même façon (par exemple en appelant la méthode getPointsOfLife).
    Mais si tu veux utiliser des fonctions propres à ton DecoratorWarElement (autrement dit, des méthodes propres au type réel de l'instance et non à son type déclaré), il faut alors préciser au compilateur que l'objet sera de type DecoratorWarElement, soit au moment de la déclaration, soit avec un dynamic_cast (sachant que cette dernière option cache très souvent des erreurs de conception, tu pourras en savoir plus en effectuant une rechercher sur le forum ou dans la FAQ).
    En clair, pour connaître le type d'un objet, le compilateur ne dispose que de sa déclaration, et pas de son instanciation, qui ne s'effectuera qu'au runtime. Il ne sait donc pas que aDestructibleElement1 sera de type DecoratorWarElement.

  5. #5
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Alors... ca me trouble un peu car je vois pas la difference entre mon code par rapport a ce qu'on fait dans le tutoriel de design patterns de developpez.com :

    http://come-david.developpez.com/tut...ecorateur#LV-1

    Quelqu'un peut m'expliquer un peu ca ?

  6. #6
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    La différence c'est que dans le tutoriel GetLimite n'est jamais utilisé.
    Pas génial ce tutoriel d'ailleurs...

    MAT.

  7. #7
    Membre averti
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Points : 410
    Points
    410
    Par défaut
    Je m'accorde à MAT.

    Il y a une règle dans le bouquin "Standard de codage en C++" de Herb Sutter et Andrei Alexandrescu qui correspond exactement à ta question.

    Je paraphrase un peu faute d'avoir le bouquin sous la main, mais cela dit en gros "N'hérite pas pour généraliser, mais pour spécifier".

    Il ne faut pas utiliser l'héritage pour rajouter des fonctionnalités, mais pour faire évoluer des fonctionnalités qui existent. C'est vrai dans le cas de décorateurs, mais c'est aussi vrai dans tous les autres cas.

    L'objet de base doit présenter l'ensemble des fonctions qui te seront utiles plus tard. Dans l'exemple auquel tu te réfères, l'objet BaseAliment présente déjà les fonction GetName() et GetPrix(), et les décorateurs ne font que les faire évoluer.

    Pour ton modèle de données, je vais te proposer quelque chose, sans remettre en cause ton idée d'utiliser les décorateurs. Je suppose que tu as de bonnes raisons de faire cela, et je ne te demande pas de les détailler.

    Je propose donc que tu prennes le problème par l'autre bout : Commence par trouver (si c'est possible) l'ensemble des points commun de tes destructibles éléments. Quelque chose comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class BasicElement{
         ... explose();
         ... attaque(BasicElement*);
         ... bouge();
    //     ... reagitAlAttaque(Attaque*); // mettons que j'oublie cette fonction
         ... resteLaSansRienFaire();
         ... que sais-je encore
    };
    Tu définis donc un objet purement fictif qui saura réagir (en ne faisant rien) à toutes les situations dans lesquelles tu vas mettre tes autres objets.

    Ensuite, tu peux le "décorer" en lui ajoutant une arme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class ElementArme : public BasicElement{
         ... attaque(BasicElement*);
    };
    ou en lui ajoutant une mobylette
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class ElementVehicule : public BasicElement{
         ... bouge();
    };
    ou encore en lui permettant de se cacher quand il n'a rien à faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class ElementQuiSeCache : public BasicElement{
         ... resteLaSansRienFaire();
    };
    Comme tu le vois, cela suppose un engagement FORT de ta part au moment où tu crée ta classe initiale. S'il existe un cas que tu n'as pas prévu au départ, tu ne pourras pas le rajouter par la suite.

    Dans mon exemple, je n'ai pas prévu que je pourrais avoir des éléments qui essaient de se cacher lorsqu'on les attaque car je n'ai pas prévu la fonction reagitAlAttaque.

    Je répète et j'insiste : l'héritage ne doit pas servir à rajouter des fonctionnalités qui ne sont pas prévues dès le départ, il doit servir à spécialiser des fonctionnalités qui ont été prévues.

    Bonne journée.

    [EDIT]
    Créer un "beau" modèle de donnée suppose aussi que tu aie une définition claire des concepts que tu utilises.
    Il n'est pas utile, mais il est très couteux de faire des choses trop génériques.
    Imaginons que tu fasses un jeu de stratégie avec des bâtiments et des armées.
    Il existe sans doute un ancêtre commun aux bâtiments et aux soldats. En effet, les soldats comme les bâtiments réagissent quand on les attaque. Par contre, il existe aussi des différences entre les deux, par exemple on ne peut pas donner à un bâtiment ordre de bouger (imaginons qu'on ne puisse pas).

    Dans le modèle que je te propose ci-dessus, on peut envisager au moins deux solutions :
    - soit tu crée deux objets Batîment et Armee qui dérivent tous les deux de "Unite de base", et pour chacun d'entre eux tu crée des décorateurs. Ainsi le décorateur ElementVehicule dériverait de Armee, auquel cas tu ne pourras jamais donner une mobylette à un bâtiment.

    - soit tu crée un objet de base NimporteQuelElement, et tu crée deux décorateurs : Batiment et Armee. Tu auras les mains plus libre. Tu pourras par exemple donner une mobylette et un bazooka à une église. Par contre, cela alourdira énormément ton modèle. Notamment, dans la mesure où tu veux que tes Bâtiments n'aient pas l'ordre "bouger" s'ils n'ont pas une mobylette, cela veut dire que tu auras une méthode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ...GetListeDesOrdres();
    Dans ta classe NimporteQuelElement et que celle-ci sera surchargée à la fois dans Batiment et dans Armee.

    En écrivant ton modèle de donnée, tu réponds donc à des questions comme "est-ce qu'il existera des bâtiments qui peuvent bouger", même si tu n'en a pas conscience.

    La solution la plus compliquée n'est la bonne QUE lorsqu'elle est nécessaire. Si aucun de tes bâtiments ne bouge, cela ne sert à rien de rajouter n lignes de codes et de complexifier ton code. Notamment, si tu réponds "oui" à cette question, cela complexifie toutes les questions suivantes. En effet, il est plus simple de répondre aux questions :
    "est-ce que les bâtiments peuvent être upgradés ?" (oui => on crée une fonction ugrade)
    "est-ce que les armées peuvent être upgradés ?" (non => on ne crée pas une fonction ugrade)
    plutôt que à la question
    "est-ce que les bâtiments/armées peuvent être upgradés ?" (ça dépend, il faut donc créer une fonction upgrade dans la classe NimporteQuelElement et la surcharger dans Batiment et dans Armee)
    [/EDIT]

  8. #8
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Merci bcp Feriaman !!!

    Ton explication est precise, claire et etandue, merci d'avoir pris la peine de m'aider comme ca, c'est vraiment cool !

    Mais je me pose maintenant une question ....

    Dans l'exemple du turoriel, on fait une instaciation de la classe baseAliment :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    BaseAliment *c=new Aliment(1.5,"Gauffre");
    Alors que la classe est abstraite comme on peut voir dans la declaration du destructeur...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    class BaseAliment 
    {
     protected:
        //les données de la classe de base
        double m_prix;
        std::string m_name;
     
    	virtual ~BaseAliment()=0;
    	BaseAliment(double prix=0,const std::string& name="");
     
     public:
     
        virtual double      GetPrix() const;
        virtual std::string GetName() const; 
     
        virtual void Afficher(std::ostream & flux) const;
    };
    En plus, le destructeur de cette classe est implemente...

    Est-ce qu'il s'agit d'une erreur ?

  9. #9
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    un destructeur virtuel doit toujours être implémenté car si tu le ne fait pas, lors de l'appel du destructeur par le destructeur des classes dérivées, tu va appeler une méthode virtuelle pure ce qui est un comportement indéfini.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  10. #10
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Merci bcp pour votre aide !!

    J'ai compris et implemente selon vos indications et cela a marche !

    Mais maintenant je me pose d'autres questions ....

    J'imagine que l'interet d'implementer des decorateurs est d'implementer une factory par la suite qui soit capable d'instancier des "objets composes" par l'accumulation de l'application de plusieurs decorateurs.

    Par example si on a un objet vehicule et un decorateur bateau et un autre avion, ce qui serait cool serait de pouvoir creer un objet hydroavion par aplication d'un decorateur avion et un decorateur avion sur l'objet vehicule. Et en suite enregistrer "hydroavion" dans une factory pour pouvoir faire un new de cet objet autant de fois qu'on veut...

    Sous cette logique j'ai essaye de l'implementer pour mon code mais cela n'a pas marche... je pense que je tombe sur le meme piege qu'au debut par rapport a l'heritage ...

    Dans le code que je joins en piece jointe, dans TestDecorator.cpp,mon probleme c'est qu'une fois cree mon entite par composition de decorateur je l'enregistre a l'aide d'une factory pour en suite creer 2 "news" objets "aCreatedWarElement1 et aCreatedWarElement2". Quand je fais que l'objet 1 attaque l'objet 2, la methode appele est la methode definie en tant que WarElement et pas en tant que Arc.
    (Aussi il a fallu que je fasse un dynamic_cast etant donne que mon objet de base est un DestructibleElement qui ne contient pas la methode attack(). Je sais que c'est un peu sale, mais c'est juste un test. Peut-etre cela est aussi la source conceptuelle de mon pb...)

    Quelqu'un peut me donner un coup de main et m'expliquer comment resoudre ce type de cas ?

    Merci
    Fichiers attachés Fichiers attachés

  11. #11
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Pouvez vous me donner une piste, me dire si c'est une sacre betise ce que je dit, me donner un example ou me donner l'adresse d'un documment qu'illustre ce que je cherche... ?

    svp

  12. #12
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Y'a pas de 'main' dans le code de l'archive, c'est donc difficile de comprendre ce que tu cherches à faire...
    Pourrais-tu donner un bout de code qui instancie ce dont tu parles et qui fait l'appel à "attack" ?

    MAT.

  13. #13
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Ma "main" est Elemtent/War/TestDecorator.cpp

  14. #14
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Je pense que tu devrais commencer par un exemple très simple, et une fois que ça fonctionne l'augmenter progressivement.

    En gros, si j'ai bien saisi :
    . DecoratorArc::Clone est implémenté à l'aide du constructeur par copie, il manque donc le constructeur par copie de Decorator qui doit rappeler Clone sur son _baseWarElement
    . il vaudrait mieux implémenter tous les constructeurs par copie dans la hiérarchie (plutôt que laisser les implémentations générées par défaut par le compilateur) et les mettre en 'protected' pour éviter de les copier directement par mégarde sans passer par Clone (problème de slicing etc...)
    . Decorator ne rappelle qu'une partie des méthodes de WarElement sur son _baseWarElement, entre autres son implémentation de attack ne fait rien, il faut rappeler systématiquement la même méthode sur le décoré
    . cherche des informations sur le pattern prototype qui est ce que tu implémentes avec ta fabrique, ça peut peut-être te donner des idées
    . pour éviter le dynamic_cast le plus simple serait de faire en sorte que la factory serve à créer des WarElement directement

    MAT.

  15. #15
    Membre averti
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Points : 410
    Points
    410
    Par défaut
    Citation Envoyé par Mat007 Voir le message
    Je pense que tu devrais commencer par un exemple très simple, et une fois que ça fonctionne l'augmenter progressivement.
    Je suis forcément d'accord avec ça.
    Surtout quand on débute, il faut compiler souvent et ne pas écrire trop de code d'un coup.

    [EDIT]
    J'ai lu une partie de ton code, et il me semble clair que tu mélange trop de concepts d'un seul coup.

    Faire une factory c'est juste pratique, ça permet d'éviter d'avoir à écrire des lignes comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    aWarElement1 = new DecoratorArc(
    	new DecoratorBottes(
    		new DecoratorPeutRevenirCommeFantome(
    			new DecoratorMortel(
    				new DecoratorPeutSeCacherDerriereUnArbre(
    				)
    			1),
    		0.2),
    	,"rouges",CUIR),
    ,200,3);
    Mais il est clair que tu n'as pas bien compris comment hériter une fabrique. Aussi, si j'étais toi, je commencerais par renoncer à ce concept, en faisant par exemple une fonction toute bête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    WarElement* CreateArcher()
    {
    	return new DecoratorArc(
    		new DecoratorBottes(
    			new DecoratorPeutRevenirCommeFantome(
    				new DecoratorMortel(
    					new DecoratorPeutSeCacherDerriereUnArbre(
    					)
    				1),
    			0.2),
    		,"rouges",CUIR),
    	,200,3);
    }
    Méfie toi des Design Pattern.
    Les Design Pattern ne sont que des noms que tu colles sur des modélisations pour pouvoir en parler avec d'autres informaticiens. En aucun cas les Design Patterns ne peuvent réfléchir à ta place.

    En tant que concepteur objet, tu dois te poser les bonnes questions et trouver les meilleures modélisations possibles pour répondre à ton problème. Les Design Pattern ne te serviront qu'à nommer ces modélisations. Ils ne te serviront pas à savoir quels sont les bonnes modélisations pour ton problème.
    [/EDIT]

  16. #16
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Oui Feriaman, faire une metode qui fasse une creation d'un element en encapsulant des decorateurs c'est une possibilite que j'avais deja envisage... (merci encore por tes explications qui sont super parlantes et explicites )

    Méfie toi des Design Pattern.
    Les Design Pattern ne sont que des noms que tu colles sur des modélisations pour pouvoir en parler avec d'autres informaticiens. En aucun cas les Design Patterns ne peuvent réfléchir à ta place.
    Je suis d'accord que les "Design Pattern" ne reflechissent pas a la place du developpeur, mais j'ai entendu que ce sont des standards pour faire face a des problemes typiques qui se presentent souvent et qu'il y a des facons plus ou moins definies de pouvoir les coder (bien qu'il peut y avoir plusieurs facons de coder un meme pattern). Quelqu'un peut nous donner son avis par rapport a ca ?

    Je pense que tu devrais commencer par un exemple très simple, et une fois que ça fonctionne l'augmenter progressivement.
    Ok, je pense que tu as egalement raison Mat007. J'ai essaye d'appliquer la combinaison d'une factory avec des decorateurs dans un exemple pedagogique que j'avais fait evoluer et ca est devenue peut-etre trop complexe pour faire ce type de chose pour une premiere fois.

    Selon le manuel dont j'ai parle auparavant, on parle de la combinaison des factories et des decorateurs comme si c'etait un outil tres puissant ... mon objectif c'est d'apprendre a les utiliser et les combiner.
    Quelqu'un peut me donner un example de cette combinaison avec un exemple plus simple ?

    Merci

  17. #17
    Membre averti
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Points : 410
    Points
    410
    Par défaut
    Citation Envoyé par donkeyquote Voir le message
    ...mais j'ai entendu que ce sont des standards pour faire face a des problemes typiques qui se presentent souvent et qu'il y a des facons plus ou moins definies de pouvoir les coder (bien qu'il peut y avoir plusieurs facons de coder un meme pattern). Quelqu'un peut nous donner son avis par rapport a ca ?
    Cet avis n'engage que moi, mais : oui et non.

    En général, les DP sont les solutions les plus utilisées à des problèmes courants. Mais la démarche :
    - identifier le problème
    - l'assimiler à un problème courant
    - en déduire le DP approprié

    N'est pas la bonne, en particulier parce qu'elle supprime la phase de réflexion. A titre d'exemple, je maintiens actuellement un moteur qui parcours des arbres. Et bien la création de ces arbres a été codée en utilisant .... le visiteur. Je ne te raconte même pas le bordel que c'est : un visiteur qui crée un arbre au fur et à mesure qu'il le visite...

    Une démarche qui me semble préférable est :
    - identifier le prolème
    - proposer la solution qui me semble la meilleure
    - éventuellement me rendre compte qu'il s'agit d'un DP

    Cette démarche te propose beaucoup plus de libertés, elle te laisse, entre autre, le loisir de te tromper, et aussi celui de trouver une solution plus adaptée à ton problème qu'un DP.

    Ceci n'empêche pas qu'il est toujours très instructif de connaitre les DP, et d'en avoir codé quelques exemple pour s'entrainer, comme tu le fais actuellement. Ceci afin de bien maitriser les concepts sous-jacents.

    Prenons un exemple. Si je te pose le problème suivant :
    Construit moi un arbre dont chaque nœud a, au plus, 4 fils
    Imaginons que tu ne connaisse pas les DP. Et bien tu vas me proposer une structure de nœud qui t'appartiendra et qui ressemblera au minimum à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class Noeud{
    	Noeud* fils[4];
    };
    Et bien certes ce n'est pas la forme la plus usuelle du DP composite, mais tu en as tiré l'essentiel, ce qui t'intéresse.
    Tu aurais aussi bien pu sortir le composite d'un catalogue, mais il aurait sans doute été moins adapté à ce que tu veux faire.

    Après, si tu as déjà utilisé des composites et si tu comprends bien le concept, tu vas sans-doute pouvoir en extraire ce qui t'intéresse vraiment. Les questions :
    - est-ce qu'il est intéressant d'avoir un objet Feuille qui n'aurait pas de fils et qui diffèrerait de tes nœuds normaux ?
    - est-ce qu'il est intéressant d'hériter ton nœud composite d'un nœud virtuel ?
    - quelles sont les méthodes qui permettent d'accéder aux fils d'un nœud ?

    dépendent du problème que tu dois résoudre. Et tu dois te les poser.

    Bon, j'arrête d'enfoncer le clou, je pense avoir clairement exprimé mon opinion.



    Citation Envoyé par donkeyquote Voir le message
    Quelqu'un peut me donner un example de cette combinaison avec un exemple plus simple ?
    Il y a plusieurs choses intéressantes dans le concept de "Factory". Le plus souvent, le seul intérêt est de factoriser du code. Dans ce cas-là, il suffit de coder dans une classe toute une série de méthodes qui ressemblent à que je te proposais, plus haut.

    Cependant, il s'agit alors d'une factory "en dur", c'est à dire qui crée toujours les mêmes objets. Cela suffit presque toujours.

    Tu peux aussi vouloir une factory dynamique, comme celle que tu as essayé de coder dans ton exemple. Mais cela veut dire que les objets que tu veux créer ne sont pas connus à l'avance.

    Par exemple, en reprenant les objets BasicElement, ElementArme, ElementVehicule... que je proposais plus haut, on peut imaginer que tu fais un wagame en deux phases :

    - phase 1 : le joueur fabrique 3 types d'unités dont il disposera par la suite. Pour chacune, il décide d'un véhicule, d'une arme, de capacité spéciales
    - phase 2 : le joueur reçoit un certain nombre de ces unités et il doit les utiliser pour vaincre celles de l'autre

    Dans ce cas-là, non-seulement l'utilisation des Décorateurs est tout à fait justifiée (puisque tu ne peux pas coder en dur à l'avance tous les types d'unités), mais en plus il te faut une factory dynamique (pour la même raison).

    Auquel cas, tu peux modifier ton élément de base comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <class T> class Prototype
    {
    	public:
    	virtual ~Prototype(){}	
    	virtual T* Clone() const =0 ;
    };
    class BasicElement : public Prototype<BasicElement >{
         ... 
    };
    Et tu devras ajouter une méthode Clone à chacun de tes décorateurs, qui prend bien soin de copier les variables spécifiques à ce décorateur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class ElementVehicule : public Decorator{
    protected : 
    	int vitesse;
    public : 
    	virtual BasicElement* Clone() const;
    	virtual void bouge();
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ElementVehicule::Clone() const
    {
    	BasicElement* base = m_Base.Clone(); 
    	return new ElementVehicule(base,vitesse);
    };
    Et du coup ta Factory ressemble à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Factory{
    	...
    	std::map<int,BasicElement*> availableUnits;
    	...
    	BasicElement* Create(int id);
    };
    En rédigeant ceci, je me rend compte que je me suis trompé plus haut en écrivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    class ElementVehicule : public BasicElement{
    	... bouge();
    };
    Il fallait bien sur lire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class Decorator: public BasicElement{
    	BasicElement m_Base;
    };
    class ElementVehicule : public Decorator{
    	... bouge();
    };

  18. #18
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut
    Les gars j'ai suivi les conseilles que vous m'avez donne pour la creation de la factory pour l'application du decorateur "DecoratorArc" ...

    C'est vraiement la catastrophe ... j'arrive pas...

    Et pourtant j'ai suivi strictement les indices que vous m'avez donne.

    Ca marche pour le decorateur "DecoratorUpgradable" mais pas pour "DecoratorArc".

    J'ai trace avec gdb et l'appel de la methode attack passe par celle que je declare comme virtuelle dans la classe de base WarElement, c'est a dire, il n'y a pas de polimorphisme et c'est ca que je n'arrive pas a comprendre pourquoi ca marche pas... pourquoi ca marche dans le cas de "DecoratorUpgradable" et pas dans le cas du "DecoratorArc" ?

    Je joins mes modifs en piece jointe. Si vous pouvez jetter un coup d'oeil rapide et me dire ce que je fais mal je vous remercierais.


    La main se trouve dans le repertoire War et elle s'appele TestDecorator.cpp, les decorateurs se trouvent sur le repertoire War, et la factory dans le repertoire Factory.

    Merci d'avance.

    PS : merci encore une fois Feriaman pour tes super explications si didactiques
    Fichiers attachés Fichiers attachés

  19. #19
    Membre averti
    Inscrit en
    Novembre 2006
    Messages
    362
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 362
    Points : 410
    Points
    410
    Par défaut
    Salut,

    Je suis un peu mécontent de ta façon de travailler. Et du coup j'hésite à te répondre. Il semble évident que tu manipules trop de concepts d'un seul coup, et que tu te perds.

    Cette méthodologie ne te mènera sans doute nulle part, dans quelques jours tu auras un nouveau problème et tu seras à nouveau obligé de demander de l'aide.

    Du coup, je te propose une nouvelle façon de travailler. Je te donne la réponse sans explication et tu dois trouver cette epxlication : Si cela ne marche pas c'est parce que ta méthode Clone n'est pas virtuelle.

    Si tu as à nouveau des problèmes avec ce projet, je ne te répondrais qu'à la condition que tu ai expliqué clairement et de façon détaillée pourquoi passer la méthode clone en "virtuel" a résolu le problème.

    Cela te convient-il ?

  20. #20
    Membre du Club Avatar de donkeyquote
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    195
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 195
    Points : 52
    Points
    52
    Par défaut


    Ok grand guru ...

    ... d'accord j'arrete de deconner

    En tout cas merci de ton aide, et oui ca me va ta proposition pour la facon de travailler. Ce-ci n'est ni un projet d'etudes ni un projet professionnel, ma seule motivation est d'ameliorer ma technique de programmation et d'apprendre nouvelles choses (ca m'amuse et ca peut etre aussi interessant pour le boulot ).

    Sinon, j'ai reflechi un peu a la solution que tu me proposes.

    En fait la methode Clone doit etre virtuelle parce que la factory doit "creer le bon objet".

    Neanmoins, a mon avis, ca ne resolu pas entiere le probleme. La solution serait plutot un truc du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    ...
     
    	WarElement * aWarElement2 = new WarElement(100, 15); // Points of life 100, Power 15 
     
    	aWarElement2 = new DecoratorArc(*aWarElement2, 200, 100); //Points of life upgrade 200 / Power 60
     
    	//instanciating factory
    	WarElementFactory aFactory;
     
    	//registering the 'type'
    	aFactory.Register("Archer", aWarElement2);
     
    	//Creating objects throught the Factory
    	//DestructibleElement * aCreatedDestructibleElement = aFactory.Create("Archer");
    	WarElement * aCreatedWarElement1 = aFactory.Create("Archer");
    	WarElement * aCreatedWarElement2 = aFactory.Create("Archer");
     
     
    	std::cout<<"Points of life of aCreatedWarElement1 before attack " <<aCreatedWarElement1->getTotalPointsOfLife()<<std::endl;
    	aCreatedWarElement2->attack(aCreatedWarElement1);		
    	std::cout<<"Points of life of aCreatedWarElement1 after attack " <<aCreatedWarElement1->getTotalPointsOfLife()<<std::endl;
    Maintenant ca marche .

    Mais ce qui me fait chi*r c'est de ne pas pouvoir faire appel de la methode attack() en tant qu'element de type "DecoratorArc" une fois que j'ai fait a posteriori un "wrapper" de type "DecoratorUpgradable".
    C'est-a-dire, le code suivant ne marcherait pas ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    ...
     
    	WarElement * aWarElement2 = new WarElement(100, 15); // Points of life 100, Power 15 
     
    	aWarElement2 = new DecoratorArc(*aWarElement2, 200, 100); //Points of life upgrade 200 / Power 60
     
    	aWarElement2 = new DecoratorUpgradable(*aWarElement2);
    	aWarElement2->lifeUp(0.2); //+20% life up
    	aWarElement2->powerUp(0.3); //+30% power up
     
    	//instanciating factory
    	WarElementFactory aFactory;
     
    	//registering the 'type'
    	aFactory.Register("Archer", aWarElement2);
     
    	//Creating objects throught the Factory
    	//DestructibleElement * aCreatedDestructibleElement = aFactory.Create("Archer");
    	WarElement * aCreatedWarElement1 = aFactory.Create("Archer");
    	WarElement * aCreatedWarElement2 = aFactory.Create("Archer");
     
     
    	std::cout<<"Points of life of aCreatedWarElement1 before attack " <<aCreatedWarElement1->getTotalPointsOfLife()<<std::endl;
    	aCreatedWarElement2->attack(aCreatedWarElement1);		
    	std::cout<<"Points of life of aCreatedWarElement1 after attack " <<aCreatedWarElement1->getTotalPointsOfLife()<<std::endl;
    Est-ce qu'il y a une facon de contourner ca ?

    Merci

    PS : Feriaman, j'espere que tu pourras et voudras continuer a m'aider car j'apprecie enormement ta pedagogie.

Discussions similaires

  1. Probleme avec la copie des surfaces
    Par Black_Daimond dans le forum DirectX
    Réponses: 3
    Dernier message: 09/01/2003, 10h33
  2. Problèmes avec le filtrage des ip
    Par berry dans le forum Réseau
    Réponses: 9
    Dernier message: 30/12/2002, 07h51
  3. probleme avec la touche F10
    Par b.grellee dans le forum Langage
    Réponses: 2
    Dernier message: 15/09/2002, 22h04
  4. Probleme avec fseek
    Par Bjorn dans le forum C
    Réponses: 5
    Dernier message: 04/08/2002, 07h17
  5. [Kylix] probleme avec un imagelist
    Par NicoLinux dans le forum EDI
    Réponses: 4
    Dernier message: 08/06/2002, 23h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo