Design Pattern Strategy

Design Pattern Strategy

Description

Design Pattern Strategy C#
Présentation du pattern Strategy très connu avec un petit exemple simple à l'appui pour illustrer le concept.

Préambule

Autres design pattern:

Observer
Command

Introduction

Le Strategy pattern est l'un des pattern les plus simples à mettre en place car il est très intuitif. L'UML class diagram est effectivement peu fourni comme on peut s'en apercevoir (schéma trouvé sur http://www.dofactory.com/Patterns/PatternStrategy.aspx)

L'idée principale est de pouvoir mettre en place une certaine stratégie (par exemple, une méthode qui réalise une certaine action) et de pouvoir changer dynamiquement de stratégie au runtime. On créer donc une interface de base, appelée ici Strategy et on y ajoute une méthode qui sera la méthode qui applique notre stratégie.

Il suffit alors de créer maintenant des classes concrètes qui implémentent cette interface et qui donc redéfinisse la méthode de stratégie obtenue de l'interface.

La classe qui va utiliser la stratégie, ici le Context, possède uniquement une instance de l'interface Strategy (construite à l'aide de l'une des classes concrètes) qui appellera donc automatiquement la stratégie sélectionnée. Voyons un petit exemple, puisque c'est bien plus parlant.

Implémentation

Supposons que j'implémente un petit robot qui peut se déplacer sur une surface. Cette surface est recouverte d'une grille. J'ajoute une Checkbox pour laisser le choix à l'utilisateur de bouger le robot librement ou de le « fixer » sur les noeuds de la grille.
On effectue donc une stratégie, à savoir soit de fixer le robot sur un noeud, soit de ne rien faire.

Voici une idée d'implémentation :

/// --------------------------------------------------------
/// <summary>
/// Strategy Pattern.
/// </summary>
/// --------------------------------------------------------
public interface IPointConstrainer
{
    PointF Constrain(PointF point);
}

/// --------------------------------------------------------
/// <summary>
/// No Strategy.
/// </summary>
/// --------------------------------------------------------
public class NoGridConstraint : IPointConstrainer
{
    public PointF Constrain(PointF point)
    {
        return point;
    }
}

/// --------------------------------------------------------
/// <summary>
/// Align to the grid.
/// </summary>
/// --------------------------------------------------------
public class GridConstainer : IPointConstrainer
{
    private int _xValue = 0;
    private int _yValue = 0;

    /// --------------------------------------------------------
    /// <summary>
    /// Create a new GridConstainer.
    /// </summary>
    /// <param name="xValue">x-constraint.</param>
    /// <param name="yValue">y-constraint.</param>
    /// --------------------------------------------------------
    public GridConstainer(int xValue, int yValue)
    {
        this._xValue = xValue;
        this._yValue = yValue;
    }

    /// --------------------------------------------------------
    /// <summary>
    /// Constain a point (align to the grid).
    /// </summary>
    /// <param name="point">The point to modify.</param>
    /// <returns></returns>
    /// --------------------------------------------------------
    public PointF Constrain(PointF point)
    {
        var x = (int)Math.Round(point.X, 0);
        var y = (int)Math.Round(point.Y, 0);
        var restDivX = x % this._xValue;
        var restDivY = y % this._yValue;


        if (restDivX > 0) x -= (restDivX < (this._xValue / 2d)) ? restDivX : restDivX - this._xValue;
        else x -= ((restDivX < (-this._xValue / 2d)) ? (restDivX + this._xValue) : restDivX);

        if (restDivY > 0) y -= (restDivY < (this._yValue / 2d)) ? restDivY : restDivY - this._yValue;
        else y -= ((restDivY < (-this._yValue / 2d)) ? (restDivY + this._yValue) : restDivY);

        return new PointF(x, y);
    }
}

On créer donc une interface IPointConstrainer et deux classes concrètes NoGridConstraint et GridConstainer qui définissent la méthode Constrain.

Il faut maintenant faire une classe qui va utiliser ces Strategy. Comme je l'ai dit plus haut, il suffit de créer une instance de l'interface, on mettra donc dans cette classe, par exemple ceci :

protected IPointConstrainer _ptConst = new NoGridConstraint();

Lorsqu'on va appeler la méthode Constrain sur _ptConst, c'est donc la méthode qui ne fait rien qui va être appelée (autrement dit, le robot va se déplacer où l'utilisateur a cliqué).
Supposons maintenant que celui-ci coche la CheckBox pour forcer le robot à se positionner sur un noeud. Il suffit de réinstancier _ptConst à l'aide de notre classe qui aligne le robot sur les noeuds, comme ceci :

_ptConst = new GridConstainer (10, 10);

Maintenant, c'est la méthode qui aligne les points qui sera appelée...
On a donc réussi à changer dynamiquement la stratégie de notre robot (le robot appellera toujours Constrain, mais selon l'objet, la méthode fera deux actions différentes).

Conclusion

Peut-être le pattern le plus simple à comprendre puisqu'en fait, il utilise seulement la notion d'interface qui est très intuitive si on a l'habitude de travailler en programmation orientée objet.
Retrouvez un exemple concret de ce pattern dans ce programme

Ce document intitulé « Design Pattern Strategy » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous