Je ne vois pas l'intérêt du Design Pattern Strategy...

Le problème exposé dans ce sujet a été résolu.

En fait, j'ai plutôt l'impression que ce patron de conception devrait s'appeler "bon sens quand tu fais de la POO".

L'idée c'est de dire que tu as une classe au départ qui a tout un tas de capacités.

Par exemple (j'aime pas les métaphores à coup de magiciens et de guerriers), imaginons un programme qui doit surveiller le trafic routier et agir en cas de situation étrange.

Globalement cette classe et les instance afférantes pourrait être modélisée ainsi :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class TrafficWatchDog{
    /* quelques attributs */
    /* et les méthodes */
    public void InitWatch(){
         /* */
    }

    public void ActOnDangerousActivity(){}
    public event DangerousActivityDetectedHandler DangerousActivity;
    public void ActOnSuspiciousActivity(){}
    public event SuspiciousActivityDetectedHandler SuspiciousActivity;
    public void ActOnCongestion(){}
    public event CongestionDetectedHandler CongestionDetected; 
}

Le principe du pattern Strategy c'est de dire "en fait les ActONXXX" doivent être déléguées à des objets spécialisés qui sont capable de s'adapter à la situation.

Ce qui permettra de lancer par exemple quatre watchdog différents, un qui en cas d'activité dangereuse prévient les secours, et force le passage au rouge des feux par exemple, un autre qui ne réagira qu'à la congestion en mettant à jour les panneaux routiers etc.

Comme ces tâches sont déléguées la gestion des priorités, des droits requis pour lancer une intervention etc. est vraiment gérée ailleurs, tout ce que fait le watchdog c'est agir quand il doit et qu'il le peut. De son point de vue, il ne sait même pas comment il a agi car c'est pas sa mission.

Je ne suis pas sur de comprendre l'explication de artragis (c'est quoi ce code tout moche ? On dirait du Java… :) ), du coup, je donne mon avis.

Le Strategy n'est pas simplement la délégation ou le dispatching, mais le comportement paramétrable (mon lmghs préféré parlerait de point de variation).

Pour garder un exemple "routier", imagine que tu conduis une voiture. Pour cela, tu utilises un algorithme "conduire", qui te permet de décider comment tu accélères, comment tu ralenties, comment tu tournes, comment tu freines, etc.

Mais en fait, ton algorithme "conduire" ne sera pas toujours le même. Par exemple, quand il pleut, tu utiliseras l'algorithme "conduire prudemment". Quand tu seras ivre, ça sera l'algorithme "conduire sans les mains". Et quand tu croiseras un voiture de police, ça sera l'algorithme "conduire avec un grand sourire".

L'algorithme "conduire" peut donc changer au cours du temps, même si ça sera toujours un algorithme "conduire". En termes de POO, pour faire cela, on place les algorithmes dans des objets et en affectant un objet-algo à un objet paramétrable, on peut changer le comportement de cet objet. Chaque algo spécialisé est implémenté dans une classe dérivée de la classe "conduire" et la classe paramétrable prend un objet-alog en paramètre, qu'elle va utiliser comme si c'était un objet "conduire" (et donc sans avoir besoin de savoir quel est le type réel de l'objet-algo).

+6 -0

Merci pour vos réponses. J'avais bien compris le fonctionnement du pattern, mais la question n'est pas là. La question c'est pourquoi faire de cette manière ? Pourquoi cette manière de faire est meilleure ? Car c'est avant tout pour ça qu'on crée des patterns, pour proposer de bonnes manières de coder. Plus précisément, pourquoi écrire mes algos dans plusieurs classes au lieu de les écrire tous dans ma classe comme de simples méthodes. Ensuite, dans la méthode conduire, je fais un switch/case qui me permet de lancer la bonne méthode. Je ne vois pas du tout en quoi la façon du pattern est meilleure.

Car c'est avant tout pour ça quand crée des patterns, pour proposer de bonnes manières de coder.

gstratege

Les patterns ne sont pas nécessairement de bonnes pratiques. C'est surtout un moyen de mettre des noms communs à tout le monde pour une notion donnée et pour se comprendre.

Plus précisément, pourquoi écrire mes algos dans plusieurs classes au lieu de les écrire tous dans ma classe comme de simples méthodes. Ensuite, dans la méthode conduire, je fais un switch/case qui me permet de lancer la bonne méthode. Je ne vois pas du tout en quoi la façon du pattern est meilleure.

gstratege

Pour cette partie là, la réponse est simple : pour respecter un principe OO qui s'appelle le principe ouvert-fermé (je te laisse faire une recherche). Modifier du code existant c'est prendre le risque d'introduire des bugs dans du code qui marche et on veut éviter ça à tout prix.

Or avec un switch à modifier pour chaque version d'algorithme que tu veux mettre c'est un excellent moyen de devoir modifier plein de code à tord et à travers tout le temps.

Les patterns ne sont pas nécessairement de bonnes pratiques. C'est surtout un moyen de mettre des noms communs à tout le monde pour une notion donnée et pour se comprendre.

Ksass`Peuk

Je ne suis pas d'accord, on ne met pas des noms communs sur des notions au hasard pour s'amuser. On met des noms communs sur des notions qui représentent de bonnes pratiques.

Pour cette partie là, la réponse est simple : pour respecter un principe OO qui s'appelle le principe ouvert-fermé (je te laisse faire une recherche). Modifier du code existant c'est prendre le risque d'introduire des bugs dans du code qui marche et on veut éviter ça à tout prix.

Or avec un switch à modifier pour chaque version d'algorithme que tu veux mettre c'est un excellent moyen de devoir modifier plein de code à tord et à travers tout le temps.

Ksass`Peuk

Tu ne modifie pas le switch si tu modifie un algo mais la méthode qui implémente l'algo…

Je ne suis pas d'accord, on ne met pas des noms communs sur des notions au hasard pour s'amuser. On met des noms communs sur des notions qui représentent de bonnes pratiques.

gstratege

J'ai pas dit "au hasard pour s'amuser" j'ai dit "pour se comprendre".

Ensuite, dans la méthode conduire, je fais un switch/case qui me permet de lancer la bonne méthode. Je ne vois pas du tout en quoi la façon du pattern est meilleure. […] Tu ne modifie pas le switch si tu modifie un algo mais la méthode qui implémente l'algo…

gstratege

Si tu veux ajouter une nouvelle stratégie pour ton algo, tu ajoutes un méthode (donc modification de ta classe telle qu'a été validée), et tu ajoutes cette possibilité à ton switch (donc modification de la méthode d'appel).

Le résultat c'est bien qu'il faut que tu revalides l'interface de ta classe et ses invariants avec la modification ajoutée.

On peut ajouter à cela le fait qu'une stratégie peut très bien nécessiter un état stocké correspondant, qui n'est pas nécessairement le même selon l'algorithme et dont on ne veut pas dans la classe utilisatrice de la stratégie (pour maintenir le SRP).

Tu dois modifier le switch si tu ajoutes un nouvel algorithmes.

Et plus généralement, si tu modifiés un code existant (testé et validé) pour autre chose que corriger un bug,tu ne respectés pas l'OCP.

Le Strategy permet de modifier le comportement sans modifier le code de tes classe paramétrables ou de tes algorithmes.

+0 -0

Je ne vois en quoi le fait d'ajouter une méthode est moins bien que d'ajouter une classe. Ensuite, je dois modifier mon switch, ce changement sera réalisé même avec l'application du pattern, car il doit bien se trouver un switch quelque part qui permet d'affecter le nouvel algo, donc de toute façon on devra modifier la classe (même en utilisant le pattern). Et pour l'histoire de l'état stocké, j'ai pas compris.

Une remarque hors sujet sur le plan technique, mais qui devrait intéresser le posteur d'origine et faire plaisir aux fondateurs de ZdS :

Pour information l'OP a posé la même question sur un autre site au même moment. Je pense qu'il vaut mieux dans ce cas l'indiquer, pour éviter de disperser les énergies.

Je suis heureux de constater que sur l'autre site francophone, qui pourtant a des centaines de milliers de membres (ils disent même des millions), il n'a obtenu aucune réponse. Comme quoi, ce n'est pas la quantité qui prime, mais la qualité. Que cet exemple serve de leçon à ceux qui seraient encore tentés de perdre leur temps sur l'autre site :) .

quark67

C'est vrai que je trouve que ce site est très réactif, et ça fait plaisir.

gstratege, en fait c'est parce que tu ne connais pas le "vrai" monde de la programmation. Ce que je veux dire par là c'est qu'on ne travaille jamais vraiment seul sur un projet, donc aller modifié du code est toujours perilleux et long (il faut comprendre comment ça marche et c'est jamais facile quand c'est pas toi qui l'a codé, ou il y à longtemps). Ce qui est bien avec le pattern Strategy c'est qu'il te suffit de modifié un attribut dans ton objet pour modifier le comportement. Je vais te donner un exemple de l'utilisation que j'en ai fait en projet tutoré (groupe de 6, travail sur 8 semaines).

On a une classe poisson qui représente un poisson. Ce poisson peut avoir plusieurs comportement : aggressif, passif, gigolo ou gros mangeur. Chaque poisson peut changer son comportement en fonction de ceux qui l'entoure mais aussi de ce qu'il a déjà fait.

L’intérêt d'un pattern strategy ici, c'est que dans la méthode "prochaine action", il me suffit de faire comportement.next(); là ou toi tu aurais beaucoup de elseif. En terme de performance c'est meilleur, en terme de compréhension de code c'est meilleur. En terme de maintenance c'est indiscutablement très largement supérieur (tu remarqueras tout les comparatifs). Bref, en POO il n'y à que des avantages à faire comme ça. Si on regarde une manière d'utiliser ce pattern dans un autre langage comme le C, je pense qu'on peut rapidement le comparer à un tableau de pointeur de fonction que tu utilise pour changer le comportement de ton programme. Rapidement hein, j'insiste.

Je ne suis pas d'accord, on ne met pas des noms communs sur des notions au hasard pour s'amuser. On met des noms communs sur des notions qui représentent de bonnes pratiques.

gstratege

Techniquement, ce n'est pas juste. Il y a une différence entre pratique beaucoup utilisé et bonne pratique, l'un peut être l'autre mais l'un peut aussi ne pas être l'autre. Un exemple est les singletons : elles sont admises mauvaises pratiques, bien qu'elles soient nommé. C'est donc un antipattern, mais toujours un design pattern.

Un design pattern est donc une solution à un problème commun. Ça implique la possibilité d'avoir plusieurs solutions à un même problème. Toutes ces solutions ont des avantages et des inconvénients qui seront à considérer en fonction de la situation.

My two cents.

il doit bien se trouver un switch quelque part qui permet d'affecter le nouvel algo

Non, justement. Pour affecter une nouvelle stratégie, tu modifies juste un attribut. En interne, la classe utilisatrice de la stratégie ne connaît pas, et n'a pas à connaître quelles sont les différentes stratégies qui existent.

ON peut toujours utiliser un modèle où, au lieu de passer un objet algo en paramètre d'un setter, on passe un ID (un nombre ou une string) qui correspond à une stratégie donnée. Mais dans ce mode, on ne code surtout pas en dur toutes les correspondances ID>objet algo. Pour ça on a le pattern factory et typiquement une toute autre partie du code s'occupe d'enregistrer les algos existants dans une collection de factories avec leurs ID correspondants.

+0 -0

Dans les exemples que l'on t'a donné, ce n'est pas un simple déplacement du switch, tu peux ne plus avoir de switch (utilisation de factory, d'events, de machine à états, n'importe quoi en fait).

Pour le principe de base, on est d'accord ? Tu as bien compris que l'on pouvait changer le comportement d'une classe (utilisatrice d'un algo) sans modifier son code ? Et que l'on pouvait implémenter un nouvel algo sans modifier le code de la classe utilisatrice, la classe algo de base ou les autres algos déjà implémenté ?

As tu pris le temps de voir l'OCP ?

+0 -0

J'ai bien compris qu'on ne touche plus à la classe utilisatrice des algos, mais ce n'ai pas une victoire en soi, car on sera obligé de toute manière de toucher du code ailleurs (que ce soit un switch ou n'importe quoi, on ne peux pas échapper au fait de devoir changer du code quelque part).

Peut être qu'un exemple concret sera bien.

Et sinon, c'est quoi l'OCP ?

Non, c'est là toute l'astuce du design pattern !

En gros tu as une interface (au sens Java, si tu connais pas c'est une classe non codée) Conduire. Dans ta classe voiture tu as un attribut du type Conduire. Le truc c'est que cette classe ne peut pas être implémentée. Maintenant, à chaque fois que tu veux créer une nouvelle manière de conduire, tu crée une classe qui implémente l'interface (si tu ne connais pas Java, c'est une classe fille tout bêtement). Donc en fait, tu ne modifie aucun code à chaque fois que tu veux créé un nouveau comportement. Après, comment tu modifie le comportement d'un objet ? Eh bien il te faut une méthode dans ta classe qui utilise l'interface Conduire qui premettent de changer la valeur de l'attribut. Après comment fonctionne la méthode, ça c'est une autre histoire. C'est très souvent fait vià une factory ou un setter.

Merci Ricocotam, mais tu ne fais que de me réexpliquer le fonctionnement du pattern et non pas pourquoi cette manière de faire est meilleure que celle que j'avais décrit.

gstratege

Pour la réutilisabilité de la chose. Toute personne qui voudra étendre le fonctionnement de ta classe ne voudra/pourra pas forcément aller ajouter des choix aux switch de chacune de tes méthodes. Imaginons que tes classes soient embarquées dans une bibliothèque, il est plus facile de laisser la possibilité aux développeurs de s'interfacer ainsi avec tes classes qu'en leur demandant d'aller toucher au code source de la lib.

OCP = open close principle (principe ouvert-fermé, dont on t'a déjà parlé). Peut être que le problème vient de là, il te manque des infos en plus du DP Strategy.

Les DP ne sont que des mises en application des principes de programmation (encapsulation, Demeter, SOLID, etc. cf les pages wiki correspondantes), qui ont pour but de renforcer la qualité logiciel (idem, cf la page wiki correspondante).

L'approche avec le switch ne respecte pas l'OCP (comme on te l'a dit), ce qui diminue la qualité logicielle (moins évolutif, moins maintenable, moins testable, etc), donc c'est moins bien (toute proportion gardée. Si ton switch permet de choisir entre un tri ascendant ou un tri descendant par exemple, il n'est pas nécessaire de créer un DP Strategy, ça sera de la sur-conception)

+2 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte