Comment ça marche, à quoi ça sert et quand faut-il l'utiliser?
Expliquons le modèle de stratégie de manière simple:
Vous avez une classe Car()
avec une méthode run()
, vous l'utilisez donc de cette façon dans un pseudo langage:
mycar = new Car()
mycar.run()
Maintenant, vous souhaiterez peut-être modifier le comportement de run()
à la volée, pendant l'exécution du programme. Par exemple, vous voudrez peut-être simuler une panne de moteur ou l'utilisation d'un bouton "boost" dans un jeu vidéo.
Il existe plusieurs façons de faire cette simulation: utiliser des instructions conditionnelles et une variable d'indicateur est une façon. Le modèle de stratégie en est un autre: il délègue le comportement de la méthode run()
à une autre classe:
Class Car()
{
this.motor = new Motor(this)
// passing "this" is important for the motor so it knows what it is running
method run()
{
this.motor.run()
}
method changeMotor(motor)
{
this.motor = motor
}
}
Si vous voulez changer le comportement de la voiture, vous pouvez simplement changer le moteur. (Plus facile dans un programme que dans la vraie vie, non? ;-))
C'est très utile si vous avez beaucoup d'états complexes: vous pouvez les modifier et les maintenir beaucoup plus facilement.
Le modèle de stratégie est utilisé pour résoudre des problèmes qui pourraient (ou sont prévus) pourraient être mis en œuvre ou résolus par différentes stratégies et qui possèdent une interface clairement définie pour de tels cas. Chaque stratégie est parfaitement valide en elle-même, certaines des stratégies étant préférables dans certaines situations qui permettent à l'application de basculer entre elles pendant l'exécution.
namespace StrategyPatterns
{
// Interface definition for a Sort algorithm
public interface ISort
{
void Sort(List<string> list)
}
// QuickSort implementation
public class CQuickSorter : ISort
{
void Sort(List<string> list)
{
// Here will be the actual implementation
}
}
// BubbleSort implementation
public class CBubbleSort : ISort
{
void Sort(List<string> list)
{
// The actual implementation of the sort
}
}
// MergeSort implementation
public class CMergeSort : ISort
{
void Sort(List<string> list)
{
// Again the real implementation comes here
}
}
public class Context
{
private ISort sorter;
public Context(ISort sorter)
{
// We pass to the context the strategy to use
this.sorter = sorter;
}
public ISort Sorter
{
get{return sorter;)
}
}
public class MainClass
{
static void Main()
{
List<string> myList = new List<string>();
myList.Add("Hello world");
myList.Add("Another item");
myList.Add("Item item");
Context cn = new Context(new CQuickSorter());
// Sort using the QuickSort strategy
cn.Sorter.Sort(myList);
myList.Add("This one goes for the mergesort");
cn = new Context(new CMergeSort());
// Sort using the merge sort strategy
cn.Sorter.Sort(myList);
}
}
}
L'utilisation de ce modèle facilite l'ajout ou la suppression de comportements spécifiques, sans avoir à recoder et retester tout ou partie de l'application;
Bonnes utilisations:
Directement à partir de article Wikipedia sur le modèle de stratégie :
Le modèle de stratégie est utile dans les situations où il est nécessaire d'échanger dynamiquement les algorithmes utilisés dans une application. Le modèle de stratégie vise à fournir un moyen de définir une famille d'algorithmes, d'encapsuler chacun comme un objet et de les rendre interchangeables. Le modèle de stratégie permet aux algorithmes de varier indépendamment des clients qui les utilisent.
Un modèle étroitement lié est le modèle Délégué; dans les deux cas, une partie du travail est transférée à un autre composant. Si je comprends bien, la différence entre ces modèles est la suivante (et corrigez-moi si je me trompe):
Dans le modèle Delegate, le délégué est instancié par la classe englobante (délégante); cela permet la réutilisation du code par composition plutôt que par héritage. La classe englobante peut être consciente du type concret du délégué, par ex. s'il invoque son constructeur lui-même (par opposition à l'utilisation d'une usine).
Dans le modèle Strategy, le composant qui exécute la stratégie est une dépendance fournie au composant englobant (utilisant) via son constructeur ou un setter (selon votre religion). Le composant utilisateur ignore totalement quelle stratégie est utilisée; la stratégie est toujours invoquée via une interface.
Quelqu'un connaît-il d'autres différences?
Pour ajouter aux réponses déjà magnifiques: Le modèle de stratégie a une forte similitude avec le passage d'une fonction (ou des fonctions) à une autre fonction. Dans la stratégie, cela se fait en enveloppant ladite fonction dans un objet suivi de passer l'objet. Certaines langues peuvent transmettre directement des fonctions, elles n'ont donc pas du tout besoin du modèle. Mais d'autres langages ne peuvent pas transmettre de fonctions, mais pouvez passer des objets; le motif s'applique alors.
Surtout dans les langages de type Java, vous constaterez que le type Zoo du langage est assez petit et que votre seul moyen de l'étendre est de créer des objets. Par conséquent, la plupart des solutions aux problèmes consistent à trouver un modèle; une façon de composer des objets pour atteindre un objectif précis. Les langues avec des zoos de type plus riches ont souvent des façons plus simples de résoudre les problèmes - mais les types plus riches signifient également que vous devez passer plus de temps à apprendre le système de type. Les langues avec une discipline de frappe dynamique obtiennent souvent un moyen sournois de contourner le problème également.