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 :

[C++] Demande précision sur Pattern Decorateur


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut [C++] Demande précision sur Pattern Decorateur
    Bonjour,

    Après le pattern Factory, je me pose des questions sur le pattern Decorateur

    Déjà sur le principe : On utilise ce pattern quand on veut "greffer" à un objet des caractéristiques plus complexes ou modifier certaines de ses caractéristiques de base. En assemblant, plusieurs décorateurs, on peut obtenir un objet complexe. C'est bien cela ?

    Ensuite sur son implémentation :
    Soit une classe de base Enemy. Le but est de créer toutes sortes d'ennemies ayant des caractéristiques différentes (vol, feu, plus de PV, plus puissant, ...)
    Le première idée est d'utiliser l'héritage classique + polymorphisme avec 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
    23
    24
    25
    class Enemy
    {
    virtual void attaquer() = 0;
    };
    class FlyEnemy : public Enemy
    {
    void attaquer() {cout << "j'attaque en volant" << endl} 
    };
     
    class PowerEnemy : public Enemy
    {
    void attaquer() {cout << "j'attaque avec toute ma puissance..." << endl} 
    };
     
    int main
    {
    std::vector<Enemy*> e;
    e.pushBack(new FlyEnemy());
    e.pushBack(new PowerEnemy());
     
    for(chaque ennemies du tableau)
    {
    e->attaquer();
    }
    }
    Voilà je me demandais si on pouvait améliorer cette méthode par le pattern Decorateur ?

    Merci pour vos conseils
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  2. #2
    Membre Expert

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Par défaut
    Bonsoir,

    L'objectif du pattern décorateur est le suivant : permettre l'ajout dynamique d'un ensemble de comportement à des objets.

    Il ne peut s'utiliser dans le même cas que l'héritage (comme tu le fais dans ton exemple), du moins il ne devrait pas : les cas d'utilisation sont distincts.

    Je vais essayer de reprendre ton exemple. Imaginons qu'on ai des ennemis qui peuvent être munis de différentes compétences : FireSkill, ColdSkill, PowerSkill, ect. Ainsi on peut voir un ennemi particulier comme un ennemi de base (Ennemy) auquel on ajoute divers comportements selon les compétences qu'ils possèdents. Les classes ...Skill vont donc contenir tout ce qu'il faut pour offrir les comportements liés à chaque type de compétence.

    Imagines que tu réalises ceci avec l'héritage et que tu ais beaucoup de compétences différentes. Déjà rien qu'avec ces 3 types de compétences on tombes à 3^2 = 9 classes différentes ! Je te laisse imaginer si le nombre de types de compétences est plus important.

    Alors que si les classes ...Skill sont des décorateurs de la classe Ennemy le système devient plus flexible : il n'y a pas de création d'un nombre important de classe, la création d'un ennemie n'est que la décoration via les classes ...Skill (qui seraient donc les décorateurs) d'un objet de type Ennemy. L'ajout des compétences de chaques créature devient dynamique et plus statique.

    Correction : 2^3 = 8 classes pas 9

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    L'objectif du pattern décorateur est le suivant : permettre l'ajout dynamique d'un ensemble de comportement à des objets.
    Et comparé au Stratégie ?

  4. #4
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Ok pour ces précisions.

    Le nouveau code devient :
    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    #include <iostream>
     
    using namespace std;
     
    class BaseEnemy
    {
        protected:
            int health;
            int defensive;
            std::string name;
     
            BaseEnemy(int _h, int _d, std::string _n)
                : health(_h), defensive(_d), name(_n) {};
            ~BaseEnemy();
     
        public:
            virtual int getHealth() const {return health;};
            virtual int getDefensive() const {return defensive;};
            virtual std::string getName() const {return name;};
     
            virtual void getInformations() const
            {
                std::cout << "Vie : " << health << std::endl;
                std::cout << "Defense : " << defensive << std::endl;
                std::cout << "Nom : " << name << std::endl << std::endl;
            };
    };
     
    class Enemy : public BaseEnemy
    {
        public:
            Enemy(int _h, int _d, std::string _n)
                : BaseEnemy(_h,_d, _n) {};
            ~Enemy(){};
    };
     
    class Decorator : public BaseEnemy
    {
        protected:
            BaseEnemy* base;
            Decorator(BaseEnemy* _base, int _h, int _d, std::string _n)
                : BaseEnemy(_h, _d, _n), base(_base) {};
            ~Decorator() {};
     
        public:
            BaseEnemy* getBase() const {return base;};
    };
     
    class PowerSkill : public Decorator
    {
        public:
            int getHealth() const {return base->getHealth()+health;};
            int getDefensive() const {return base->getDefensive()+defensive;};
            std::string getName() const {return base->getName()+" "+name;};
     
            void getInformations() const
            {
                std::cout << "Vie : " << getHealth() << std::endl;
                std::cout << "Defense : " << getDefensive() << std::endl;
                std::cout << "Nom : " << getName() << std::endl << std::endl;
            };
     
            PowerSkill(BaseEnemy* _base, int _h, int _d, std::string _n)
                : Decorator(_base, _h, _d, _n) {};
            ~PowerSkill() {};
    };
     
    class FireSkill : public Decorator
    {
        private:
            int powerFire;
     
        public:
            int getPowerFire() const {return powerFire; };
     
            void getInformations() const
            {
                base->getInformations();
                std::cout << "Puissance de feu : " << getPowerFire() << std::endl;
            };
     
            FireSkill(BaseEnemy* _base, int _powerFire)
                : Decorator(_base, 0, 0, ""), powerFire(_powerFire) {};
            ~FireSkill() {};
    };
     
    int main()
    {
        BaseEnemy* b = new Enemy(5, 10, "Enemy");
        b->getInformations();
     
        b = new PowerSkill(b, 2, 3, "Power Enemy");
        b->getInformations();
     
        b = new FireSkill(b, 100);
        b->getInformations();
     
      //  delete b;
     
        return 0;
    }
    Est ce la bonne implémentation du pattern ?

    J'ai vu que certain utilisait la base par référence, j'ai utilisé un pointeur je pense que ca ne change rien ?

    Et dernière chose, mon code a des fuites de mémoire et je n'arrive pas à libérer intelligemment la mémoire
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par Aspic Voir le message
    J'ai vu que certain utilisait la base par référence, j'ai utilisé un pointeur je pense que ca ne change rien ?
    Le décorateur a une durée de vie inférieur à la base et il n'est pas sensé la gérer. Le décorateur n'existe pas sans base. La référence permet de garantir ça puisqu'une référence ne peut être null contrairement à un pointeur
    Citation Envoyé par Aspic Voir le message
    Et dernière chose, mon code a des fuites de mémoire et je n'arrive pas à libérer intelligemment la mémoire
    Et oui car les durées de vie des décorateurs et des bases sont indépendantes. Il te faut donc les gérer à part explicitement. D'où une utilisation plus facile avec des références
    cf tuto de david

    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
    class Decorator : public BaseEnemy
    {
        protected:
            BaseEnemy& base;
            Decorator(BaseEnemy& _base, int _h, int _d, std::string _n)
                : BaseEnemy(_h, _d, _n), base(_base) {};
            ~Decorator() {};
     
        public:
            BaseEnemy& getBase() const {return base;};
    };
     
    void getInformations(BaseEnemy &b)
    {
      b.getInformations();
    }
     
    int main()
    {
        BaseEnemy b(5, 10, "Enemy");
        getInformations(b);
     
        PowerSkill ps(b, 2, 3, "Power Enemy");
        getInformations(ps);
     
        FireSkill fs(ps, 100);
        getInformations(fs);
     
        return 0;
    }

    [EDIT] : si tu voulais gérer ça par pointeur, dans le décorateur ce serait plus probablement un std::weak_ptr plutôt qu'un std::shared_ptr puisque les durées de vie n'ont pas à être liées.

  6. #6
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Ok pour la référence
    J'ai modifié mon code en conséquence mais ca fuit toujours :
    Citation Envoyé par rapport de valgrind
    --3140-- REDIR: 0x41db3b0 (free) redirected to 0x4025b6b (free)
    ==3140==
    ==3140== HEAP SUMMARY:
    ==3140== in use at exit: 78 bytes in 4 blocks
    ==3140== total heap usage: 9 allocs, 5 frees, 214 bytes allocated
    ==3140==
    ==3140== Searching for pointers to 4 not-freed blocks
    ==3140== Checked 101,764 bytes
    ==3140==
    ==3140== 78 (20 direct, 58 indirect) bytes in 1 blocks are definitely lost in loss record 4 of 4
    ==3140== at 0x402641D: operator new(unsigned int) (vg_replace_malloc.c:255)
    ==3140== by 0x8048B50: main (in /mnt/partage/decorateur_ennemie/test)
    ==3140==
    ==3140== LEAK SUMMARY:
    ==3140== definitely lost: 20 bytes in 1 blocks
    ==3140== indirectly lost: 58 bytes in 3 blocks
    ==3140== possibly lost: 0 bytes in 0 blocks
    ==3140== still reachable: 0 bytes in 0 blocks
    ==3140== suppressed: 0 bytes in 0 blocks
    ==3140==
    ==3140== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 17 from 6)
    --3140--
    --3140-- used_suppression: 17 U1004-ARM-_dl_relocate_object
    ==3140==
    ==3140== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 17 from 6)
    Code :
    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    #include <iostream>
     
    using namespace std;
     
    class BaseEnemy
    {
        protected:
            int health;
            int defensive;
            std::string name;
     
            BaseEnemy(int _h, int _d, std::string _n)
                : health(_h), defensive(_d), name(_n) {};
     
        public:
            ~BaseEnemy() {};
     
            virtual int getHealth() const {return health;};
            virtual int getDefensive() const {return defensive;};
            virtual std::string getName() const {return name;};
     
            virtual void getInformations() const
            {
                std::cout << "Vie : " << health << std::endl;
                std::cout << "Defense : " << defensive << std::endl;
                std::cout << "Nom : " << name << std::endl << std::endl;
            };
    };
     
    class Enemy : public BaseEnemy
    {
        public:
            Enemy(int _h, int _d, std::string _n)
                : BaseEnemy(_h,_d, _n) {};
            ~Enemy(){};
    };
     
    class Decorator : public BaseEnemy
    {
        protected:
            BaseEnemy& base;
            Decorator(BaseEnemy& _base, int _h, int _d, std::string _n)
                : BaseEnemy(_h, _d, _n), base(_base) {};
            ~Decorator(){  };
     
        public:
            BaseEnemy& getBase() const {return base;};
    };
     
    class PowerSkill : public Decorator
    {
        public:
            int getHealth() const {return base.getHealth()+health;};
            int getDefensive() const {return base.getDefensive()+defensive;};
            std::string getName() const {return base.getName()+" "+name;};
     
            void getInformations() const
            {
                std::cout << "Vie : " << getHealth() << std::endl;
                std::cout << "Defense : " << getDefensive() << std::endl;
                std::cout << "Nom : " << getName() << std::endl << std::endl;
            };
     
            PowerSkill(BaseEnemy& _base, int _h, int _d, std::string _n)
                : Decorator(_base, _h, _d, _n) {};
            ~PowerSkill() {};
    };
     
    class FireSkill : public Decorator
    {
        private:
            int powerFire;
     
        public:
            int getPowerFire() const {return powerFire; };
     
            void getInformations() const
            {
                base.getInformations();
                std::cout << "Puissance de feu : " << getPowerFire() << std::endl;
            };
     
            FireSkill(BaseEnemy& _base, int _powerFire)
                : Decorator(_base, 0, 0, ""), powerFire(_powerFire) {};
            ~FireSkill() {};
    };
     
    int main()
    {
        BaseEnemy* b = new Enemy(5, 10, "Enemy");
        b->getInformations();
     
        b = new PowerSkill(*b, 2, 3, "Power Enemy");
        b->getInformations();
     
        b = new FireSkill(*b, 100);
        b->getInformations();
     
        delete b;
     
        return 0;
    }
    J'ai été obligé de mettre le destructeur en public dans BaseEnemy pour faire un delete b; dans le main.

    Mais d'un autre côté ca ne m'étonne pas que ca fuit vu que j'ai 3 new pour un seul delete
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  7. #7
    Expert confirmé

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 033
    Billets dans le blog
    12
    Par défaut
    Mets tes destructeurs en virtual.
    Là le destructeur de BaseEnemy ne doit jamais être appelé, si je ne me trompe pas.
    Ensuite, effectivement, 3 new pour un seul delete ...
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

Discussions similaires

  1. [C++] Demande précision sur Pattern Factory
    Par Aspic dans le forum C++
    Réponses: 36
    Dernier message: 25/08/2011, 12h40
  2. [MySQL] Demande précisions sur LOCK TABLE
    Par renaud26 dans le forum PHP & Base de données
    Réponses: 8
    Dernier message: 15/04/2011, 13h49
  3. Réponses: 8
    Dernier message: 30/09/2006, 05h18
  4. Demande de précisions sur Backup/Restore et transactions
    Par lio33 dans le forum Connexion aux bases de données
    Réponses: 1
    Dernier message: 16/11/2005, 12h08
  5. [Observateur] Précisions sur le design pattern Observer [UML]
    Par joquetino dans le forum Design Patterns
    Réponses: 2
    Dernier message: 07/10/2004, 22h35

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