Si vous disposez d'ouvrages ou d'articles de référence ou si vous connaissez des sites web de qualité traitant du thème abordé ici, merci de compléter l'article en donnant les références utiles à sa vérifiabilité et en les liant à la section « Notes et références ».
Vous pouvez aider en ajoutant des références ou en supprimant le contenu inédit. Voir la page de discussion pour plus de détails.
En génie logiciel, le patron stratégie est un patron de conception(design pattern) de type comportemental grâce auquel des algorithmes peuvent être sélectionnés à la volée au cours du temps d'exécution selon certaines conditions.
Le patron de conception stratégie est utile pour des situations où il est nécessaire de permuter dynamiquement les algorithmes utilisés dans une application. Le patron stratégie est prévu pour fournir le moyen de définir une famille d'algorithmes, encapsuler chacun d'eux en tant qu'objet, et les rendre interchangeables. Ce patron laisse les algorithmes changer indépendamment des clients qui les emploient.
Utilisation
Dès lors qu'un objet peut effectuer plusieurs traitements différents, dépendant d'une variable ou d'un état[1].
Structure
Exemple en C++
#include<iostream>#include<memory>// IStrategie est l’interface permettant d’exécuter un algorithmeclassIStrategie{public:voidexecute(){process();}// NVIvirtual~IStrategie()=default;// Héritage, donc destructeur public virtuelprivate:virtualvoidprocess()=0;// IStrategie::process() est une fonction virtuelle pure// et de ce fait IStrategie est une classe abstraite// autrement dit une classe qui ne peut être instanciée};classAlgorithmeA:publicIStrategie{private:// Chaque Algorithme redéfinit la façon de procédervoidprocess()override{std::cout<<"Traitement A"<<std::endl;}};classAlgorithmeB:publicIStrategie{private:voidprocess()override{std::cout<<"Traitement B"<<std::endl;}};classAlgorithmeC:publicIStrategie{private:voidprocess()override{std::cout<<"Traitement C"<<std::endl;}};// Contexte est la classe visible par le client.// Elle fait le lien entre les demandes du client et l’algorithme (ou les algorithmes) à utiliser.classContextefinal{private:std::unique_ptr<IStrategie>strategie;public:Contexte(std::unique_ptr<IStrategie>new_strategie):strategie(std::move(new_strategie)){}voidexecute(){strategie->execute();}voidsetAlgorithme(std::unique_ptr<IStrategie>new_strategie){strategie=std::move(new_strategie);}};intmain(){Contextecontexte(std::make_unique<AlgorithmeA>());contexte.execute();// Le contexte va effectuer le traitement Acontexte.setAlgorithme(std::make_unique<AlgorithmeB>());contexte.execute();// Le contexte va effectuer le traitement Bcontexte.setAlgorithme(std::make_unique<AlgorithmeC>());contexte.execute();// Le contexte va effectuer le traitement Creturn0;}
Des idées semblables amènent à une réalisation à l'aide d'interface.
L'objet qui doit avoir une stratégie adaptable à l'exécution implémente IStrategie : la même interface que d'autres objets. L'objet principal délègue l'exécution de la tâche à un autre objet membre qui implémente IStrategie.
L'objet membre étant déclaré dans la classe comme une interface, son implémentation importe peu, on peut donc changer de stratégie à l'exécution. Cette manière de faire se rapproche du Principe de l'injection de dépendance.
usingSystem;/// <summary> La manière dont le grand général guidera ses troupes</summary>interfaceIStrategie{voidMettreEnOeuvre();}/// <summary> Ce grand homme qui fera bientôt des choix décisifs </summary>classSeigneurDeLaGuerre{/// <summary> une stratégie générique </summary>IStrategie_strategie;/// <summary> comment changer de stratégie </summary>publicIStrategieStrategie{set{_strategie=value;}}/// <summary> délégation de la tâche </summary>publicvoidPrendreLaVille(){_strategie.MettreEnOeuvre();}}classDéfoncerLePontLevisDeFace:IStrategie{publicvoidMettreEnOeuvre(){Console.WriteLine("Prendre la ville de face en défonçant le pont-levis.");}}classPasserParLaFaceNord:IStrategie{publicvoidMettreEnOeuvre(){Console.WriteLine("Prendre la ville en escaladant la muraille nord.");}}classAttendreQueLaVilleSeRende:IStrategie{publicvoidMettreEnOeuvre(){Console.WriteLine("Attendre qu'il n'y ait plus rien à manger en ville "+"et que tout le monde meure de faim.");}}classSeMarierAvecLaCousineDuDuc:IStrategie{publicvoidMettreEnOeuvre(){Console.WriteLine("Organiser un mariage avec la cousine du Duc "+"alors qu'elle rejoint la ville de retour des Baléares "+"et inviter toute la ville à une grande fête.");}}/// <summary> Différentes situations </summary>enumMétéo{IlFaitBeau,IlYADuBrouillard,IlFaitTropChaudPourTravailler,IlPleut}classProgram{staticvoidMain(){// notre acteurvarkevin=newSeigneurDeLaGuerre();// les aléas du systèmevarmétéo=(Météo)(newRandom().Next(0,4));// une liaison tardiveswitch(météo){caseMétéo.IlFaitBeau:kevin.Strategie=newDéfoncerLePontLevisDeFace();break;caseMétéo.IlYADuBrouillard:kevin.Strategie=newPasserParLaFaceNord();break;caseMétéo.IlFaitTropChaudPourTravailler:kevin.Strategie=newAttendreQueLaVilleSeRende();break;caseMétéo.IlPleut:kevin.Strategie=newSeMarierAvecLaCousineDuDuc();break;default:thrownewException("Nan finalement seigneur de la guerre c'est "+"pas cool comme job : vous décidez d'aller cueillir "+"des champignons dans le Périgord.");}// une exécution aux petits oignonskevin.PrendreLaVille();}}
Nous savons que voler() et cancaner() sont les parties de la classe Canard qui varient d’un canard à l’autre.
Pour séparer ces comportements de la classe Canard, nous extrayons ces deux méthodes de la classe et nous créons un nouvel ensemble de classes pour représenter chaque comportement[2].
Canard utilise des attributs de type interfaceComportementVol et ComportementCancan (le "bruit" du canard).
Ce sont ces interfaces qui encapsulent le code des méthodes effectuerVol() et effectuerCancan().
Ce sont ces méthodes que nous souhaitons encapsuler, selon le patron de conception stratégie, afin de les rendre interchangeables.
Malgré notre volonté de rendre ces méthodes interchangeables :
il reste d'autres méthodes que nous souhaitons conserver communes : ici la méthode nager().
et d'autres méthodes que nous souhaitons spécifique à l'implémentation choisie : ici la méthode afficher().
publicabstractclassCanard{ComportementVolcomportementVol;ComportementCancancomportementCancan;publicCanard(){}publicabstractvoidafficher();publicvoideffectuerVol(){comportementVol.voler();}publicvoideffectuerCancan(){comportementCancan.cancaner();}publicvoidnager(){System.out.println("Tous les canards flottent, même les leurres!");}publicvoidsetComportementVol(ComportementVolcomportementVol){this.comportementVol=comportementVol;}publicvoidsetComportementCancan(ComportementCancancomportementCancan){this.comportementCancan=comportementCancan;}}
Maintenant, nous allons spécialiser cette classe Canard en implémentant deux nouvelles classes héritant de Canard.
La classe Colvert :
publicclassColvertextendsCanard{publicColvert(){comportementVol=newVolerAvecDesAiles();comportementCancan=newCancan();}publicvoidafficher(){System.out.println("Je suis un vrai colvert");}}
Et la classe PrototypeCanard :
publicclassPrototypeCanardextendsCanard{publicPrototypeCanard(){comportementVol=newNePasVoler();comportementCancan=newCancan();}publicvoidafficher(){System.out.println("Je suis un prototype de canard");}}
L'attribut comportementVol est un objet dont la classe implémente l'interface ComportementVol.
Il est alors possible de spécifier autant de ComportementVol et de ComportementCancan que nécessaire, simplement en créant de nouvelle classe implémentant ces deux interfaces.
À charge ensuite à ces classes d'implémenter les méthodes voler() ...
publicclassVolerAvecDesAilesimplementsComportementVol{publicvoidvoler(){System.out.println("Je vole !!");}}publicclassNePasVolerimplementsComportementVol{publicvoidvoler(){System.out.println("Je ne sais pas voler");}}
... et cancaner() pour spécifier le comportement désiré.
Ici, le comportement d'une voiture peut être modifié en fonction de la stratégie de conduite:
ConduiteSport>>avanceTranscriptshow:'à fond à fond';crConduiteTranquille>>avanceTranscriptshow:'on roule doucement';crVoiture>>modeSportstrategieConduite:=ConduiteSportnewVoiture>>modeTranquillestrategieConduite:=ConduiteTranquillenewVoiture>>avancestrategieConduiteavance
On peut donc écrire:
maVoiture:=Voiturenew.maVoituremodeSport.maVoitureavance."Affiche 'à fond à fond'"maVoituremodeTranquille.maVoitureavance."Affiche 'on roule doucement'"
Notes et références
↑(en) Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides, Design Patterns: Elements of Reusable Software, Addison-Wesley, (ISBN0-201-63361-2), p. 316
↑Freeman, Eric, 1965-, Freeman, Elisabeth., Sierra, Kathy. et Bates, Bert. (trad. de l'anglais), Design patterns, tête la première, Beijing/Cambridge/Paris etc., O'Reilly, , 637 p. (ISBN2-84177-350-7 et 978-2-84177-350-3, OCLC181354957, lire en ligne)