SkiToo.Org

Tutoriaux Python, Ubuntu, Django, SVN, Eclipse…

[Design Patterns] Et si on parlait Stratégie ?

Aujourd’hui je vais tenter de vous expliquer comment fonctionne l’un des Design Patterns les plus utilisé qui fut initialement proposé par le GOF (Gang Of Four) : le Design Pattern Stratégie.

Pour commencer, il peut être intéressant de rappeler ce que sont les Design Pattern ou “Motifs de conception” en français. Les Design Patterns sont un ensemble de solutions standards permettant de répondre à des problèmes de conception rencontrés lors du développement de projets orientés objet. Les Design Patterns se regroupent dans trois grands groupes : Créationnel, Structurel et Comportemental. Le motif de conception que l’on va voir aujourd’hui fait partie du dernier groupe : les comportementaux.

Le Design Pattern Stratégie

Description :

L’intérêt principal de ce motif de conception est de permettre l’encapsulation d’algorithmes et de les rendre permutables. En effet, imaginons que l’on souhaite changer dynamiquement le comportement d’un objet et cela sans modifier la classe de ce dernier. A première vue, celà semble plutôt compliqué… mais il n’en est rien :)
Prenons un exemple simple : Une classe Personne qui a le dont de se déplacer. On peut imaginer plusieurs types de déplacements : marcher, courir !
De manière générale on aurait tendance à créer une méthode par déplacement :

Exemple en Python:

class Personne:
def marcher(self):
print “je marche”

def courir(self):
print “je cours”

Exemple en PHP 5 :

class Personne
{
public function marcher()
{
print “je marche”;
}

public function courir()
{
print “je cours”;
}
}

C’est pas mal, mais rien de très dynamique ! En effet, imaginons que l’on souhaite ajouter un nouveau comportement de déplacement (par exemple : nager) il faudra ajouter une nouvelle méthode.
Stratégie va permettre à notre classe Personne de posséder un comportement de déplacement dynamiquement interchangeable. Pour cela nous devons externaliser les comportements de la classe en utilisant un système bien connu en POO : la composition.
Regardons le diagramme de classe suivant :

Le diagramme UML :

Design pattern Stratégie

Ce schéma mérite une petite explication. Comme on peut le voir, la classe Personne ne possède plus les trois méthodes “marcher“, “courir” mais elles ont été remplacées par deux autres : “setMouvement” et “déplacer“. La première va nous permettre d’attribuer aux instances de la classe Personne un objet Mouvement. La seconde quant à elle va exécuter le mouvement précédemment attribué à l’objet Personne. Les comportements sont désormais des classes à part entière qui implémente l’interface Mouvement.

Exemple en python :

class Personne:
def __init__(self):
self.__mouvement = None

def setMouvement(self, mouvement):
self.__mouvement = mouvement

def deplacer(self):
if self.__mouvement:
self.__mouvement.execute()

class Mouvement:
def execute(self):
raise “Doit être implementé”

class Marcher(Mouvement):
def execute(self):
print “je marche”

class Courir(Mouvement):
def execute(self):
print “je cours”

pers = Personne()

unMouvement = Marcher()
pers.setMouvement( unMouvement )
pers.deplacer() #affiche "je marche"

unMouvement = Courir()
pers.setMouvement( unMouvement )
pers.deplacer() #affiche "je cours"
 

En Python la notion d’interface comme en Java ou PHP 5 n’existe pas. J’ai donc fait en sorte, dans mon exemple, que la méthode execute de la classe Mouvement soit obligatoirement redéfinie dans les classes qui étendent de celle-ci.

Exemple en PHP 5 :

interface Mouvement
{
public function execute();
}

class Marcher implements Mouvement
{
public function execute()
{
print “je marche”;
}
}

class Courir implements Mouvement
{
public function execute()
{
print “je cours”;
}
}

class Personne
{
private $mouvement;

public function setMouvement( Mouvement $mouvement )
{
$this->mouvement = $mouvement;
}

public function deplacer()
{
if( !is_null($this->mouvement) )
{
$this->mouvement->execute();
}
}
}

$pers = new Personne();

$unMouvement = new Marcher();
$pers->setMouvement( $unMouvement );
$pers->deplacer();// affiche "je marche"

$unMouvement = new Courir();
$pers->setMouvement( $unMouvement );
$pers->deplacer();// affiche "je cours"
 

La méthode deplacer appelle la méthode execute du mouvement passé en paramètre. On s’aperçoit dans cet exemple que comportement de la méthode deplacer est modifiable dynamiquement. Lors de son premier appel elle affiche “je marche” puis “je cours” lors du second appel. Ceci est du au fait que le mouvement passé en paramètre n’est plus le même. Imaginons désormais que l’on doivent ajouter un nouveau comportement de déplacement (par exemple : “nager”), il suffira tout simplement de créer une nouvelle classe nommée Nager qui implémente la classe Mouvement.

class Nager(Mouvement):
def execute(self):
print “je nage”

pers = Personne()

unMouvement = Nager()
pers.setMouvement( unMouvement )
pers.deplacer() #affiche "je nage"
 

Très pratique si votre boss ne vous à fourni que la version compilée de la classe Personne :) On peut modifier son comportement sans toucher une seule ligne de celle-ci.

Et voilà que ce premier tutoriel sur les Design Patterns touche à sa fin. En espérant que ça vous a plu… :)

10 Responses

  1. daeMOn Says:

    Bien sympa :)
    Ca a le merite d’être clair et de rester simple d’approche. J’attend la suite avec impatience ^^

  2. TulipeMoutarde Says:

    Très bon post!

    Par contre, je trouve la version Python pas très élégante… N’y a-t-il pas moyen de ruser autrement?

    Bonne continuation

  3. Fuse Says:

    Intéressant, je le connaissais pas celui là :)

  4. mickro Says:

    Je suis assez mitigé sur cet article.

    Autant je trouve que dans l’exemple, la technique employé est adapté au besoin du code.

    Autant je ne trouve pas que cet exemple donne une réelle idée du Design Pattern.

    On pourrait le confondre avec la bonne pratique “une et une seule fois” qui tend à ne pas écrire la même tranche de code à deux endroit. Et qui milite contre le “copier/coller”, mais ceci est une autre histoire…

    Ainsi, j’aurai souhaité, à la fin de cet article, avoir plus d’exemples sous forme de shémas UML afin de délimiter correctement l’ensemble du groupe comportemental.

    Cela dit, le thon est bon je reviendrais.

    Le troll qui sommeil en moi répond à :
    “Par contre, je trouve la version Python pas très élégante… N’y a-t-il pas moyen de ruser autrement?”

    par : en ruby c’est plus joli :)

    class Personne

    def setMouvement mouvement
    @mouvement = mouvement
    end

    def deplacer
    if @mouvement != nil
    @mouvement.execute()
    end
    end

    end

    class Mouvement
    def execute
    raise “Doit être implementé”
    end
    end

    class Marcher < Mouvement
    def execute
    puts “je marche”
    end
    end

    class Courir < Mouvement
    def execute
    puts “je cour” # y’a une faute dis donc :p
    end
    end

    pers = Personne.new

    unMouvement = Marcher.new
    pers.setMouvement unMouvement
    pers.deplacer #affiche “je marche”

    unMouvement = Courir.new
    pers.setMouvement unMouvement
    pers.deplacer #affiche “je cour”

    PS : comment on balise le dans ce machin ?

  5. SkiT Says:

    @ mickro : Je ne pense pas que ce pattern réponde correctement à la bonne pratique “une et une seule fois”, puisque son but est essentiellement d’encapsuler des algorithmes. En d’autre terme de séparer les choses qui varient de celles que ne le font pas. Dans notre exemple ce qui varie est le comportement de déplacement.

  6. mickro Says:

    … suite a discution en vrai, c’est moi qui est mal cerné l’article.

    Je l’avais prit pour une définition du groupe comportemental et forcement je le trouvais incomplet.

    Mais comme il s’agit du Design Pattern Stratégie seulement… alors ca est moins pertinent mon comment du coup :p

  7. Tulipe.Moutarde Says:

    :D
    mickro, la version ruby est exactement la même que la Python, ce qui me chifonne un peu c’est la partie:

    class Mouvement:
    def execute(self):
    raise “Doit être implementé”

    Pour simuler une interface. Mais en même temps je vois pas comment faire autrement…

  8. filt3rek Says:

    Salut !
    Je passais pas là par hasard, je suis agréablement surpris par la simplicité de l’explication, appuyé par un exemple que je trouve des plus parlants.
    Il manque en effet des artiles de cette pertinence sur la toile, dans le sens où en quelques lignes et sans salamalek, le mot barbare de Design Pattern Strategie a été démystifié.
    Pour etre honete avec la terre entiere, j’avoue (mais je rougis :p) que j’utilisais cette methode sans meme m’en rendre compte…
    Comme quoi, les Design Patterns, sont en quelques sortes une jurisprudence du code.

  9. Benji Says:

    Hé, super,
    je n’utilise jamais les DP (gloups) et là j’ai enfin un article qui e donne envie d’aller plus loin.
    Merci, mais j’en veux encore, encore ….

  10. tripless Says:

    Un grand merci à toi, je suis du même avis que benji on attend la suite (héhéhé)…

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.