P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ IF YOU'RE NOT INTERESTED IN ORAL SEX, KEEP YOUR MOUTH SHUT Articles | Connexion
 
~Le motif Stratégie

 Présentation

La diversité des situations couvertes par les motifs de conception décrits dans, notamment, l'ouvrage du Gang Of Four garantit au programmeurs des solutions élégantes pour la majorité des problèmes courants.
 Sommaire


 Introduction

Le motif de conception orienté objet Stratégie (ou simplement "Strategy" pour les anglophones) appartient au groupe de motifs comportementaux. Etudions donc ce motif relativement proche du motif Poids Mouche, ou du motif Etat que nous verrons plus tard.


 Le motif Stratégie

Ce motif définit une famille d'algorithmes et les rend interchangeables en encapsulant chacun d'entre eux. Les différents algortihmes se trouvent encapsulés par une classe "driver" appelée contexte (ou dans la terminologie internationale, Context). Le code client, soit le code source qui fait appel à votre implémentation du motif Stratégie, peut sélectionner l'un des algortihmes ou s'en voir attribuer un considéré comme le meilleur par le contexte. Le but de ce design pattern est d'octroyer l'opportunité de passer facilement d'un algorithme à l'autre en se débarrassant de structures imposantes telles que des successions de tests conditionnels (if, switch...). Lors de son utilisation, une seule stratégie se trouve instanciée à la fois au sein du contexte. Un contexte propose en effet l'emploi de plusieurs stratégies, correspondant chacune à un algorithme distinct. Le choix de la stratégie active est achevé par l'utilisateur de l'implémentation. De plus, un contexte encapsule un ensemble d'algorithmes dont les finalités se révèlent équivalentes.

En pratique, tout programme offrant une fonctionnalité pouvant se voir réalisée de plusieurs manières différentes pourra bénéficier d'une implémentation du motif Stratégie. Le choix d'une stratégie dépend de son efficacité (dans le cas par exemple de tris), des préférences de l'utilisateurs ou encore de contraintes techniques. Un des intérêts évident de ce motif réside dans sa grande souplesse : l'ajout ou la modification de stratégie s'accomplit très simplement. Les situations justifiant l'emploi de ce motif sont courantes et nombreuses. Quelques-uns d'entre elles sont citées dans le "Smalltalk Companion" :

  • Export de fichiers dans différents formats
  • Compression de données suivant divers algorithmes
  • Capture vidéos utilisant différents formats de compression
  • Affichage de statistiques sous formes d'histogrammes, de camemberts...

Nous pourrions ajouter à cela le cryptage de données suivant une méthode particulières, et ainsi de suite. Dans chacune de ces situations, le programme posséderait un contexte auquel le programme principal indiquerait quelle stratégie employer. Bien entendu, chacune des stratégie devrait posséder la même interface de programmation que ses congénères bien qu'elles ne doivent pas nécessairement dépendre de la même hiérarchie de classe. Cet aspect se voit facilement appliqué avec la notion d'héritage multiple en C++ ou Eiffel ou encore des interfaces en Java.


 Contexte, Stratégies et toutes sortes de choses

Nous allons à présent considérer l'implémentation d'un motif de conception Stratégie permettant un choix de modèles de compressions de données. La première étape consiste à définir précisémment l'interface publique commune de programmation des stratégies. Le listing numéro 1 propose un exemple primaire d'une telle interface. Le modèle retenu est ici une classe abstraite Java. Toutes les stratégies proposées dans l'application devront hériter de cette classe abstraite et implémenter la méthode inflate() destinée à la compression des données du flux "in" dans le flux de sortie "out". Ainsi le listing 2 présente une stratégie de compression au format zip.

L'emploi des stratégies nécessite la présence d'un contexte qui détermine quelle stratégie sera employée. Puisque le choix s'effectue par une demande explicite de l'utilisateur, le contexte a juste besoin de posséder une variable d'instance sauvegardant la stratégie active. Le contexte porte également la responsabilité d'encapsuler les données à traiter. Le listing 3 offre un exemple de contexte.

Munis d'un contexte et d'une ou plusieurs stratégies, nous pouvons à présent employer notre motif de conception au sein d'une application réelle. Voici un exemple de code source que nous aurions l'opportunité d'écrire dans une telle situation :

InflateContext context = new InflateContext();
String fileName = "/home/gfx/article.rtf";
context.setData(fileName, new FileInputStream(fileName),
		  new FileOutputStream(fileName + ".tar.gz"));
context.setTarGunzipStrategy();
context.inflate();
       
      
JextCopier dans Jext | Jext | Plugin Codegeek

 Derniers maux

Les design patterns Stratégie vous permettent de sélectionner un algorithme parmis plusieurs de manière dynamique. Ceux-ci peuvent posséder des liens hiérarchiques d'héritage ou au contraire ne posséder aucun lien. La seule nécessité concerne l'implémentation d'une interface de programmation commune. L'approche introduite par la notion de contexte rend votre code source bien plus flexible et lisible que si vous deviez sélectionner vous-mêmes la classe dérivée appropriée.

Néanmoins, cette conception impose au programmeur client de connaître toutes les possibilités offertes par votre contexte. Il devra ainsi définir ses propres critères de choix parmi les divers choix qui s'offrent à lui. La décision peut également se voir déléguée jusqu'à l'utilisateur. Nous avons observé une situation correspondant à une délégation du choix vers l'utilisateur en étudiant un contexte de compression de fichiers. Il en serait de même pour le choix de l'affichage de statistiques.

Enfin, puisque plusieurs paramètres peuvent être passés aux différents algorithmes, vous devez concevoir un contexte et des stratégies suffisamment ouverts pour accepter des arguments inutiles. Ainsi, notre contexte de compression pourrait définir une méthode setCompressionLevel(int level) qui ne serait employée que par les stratégies ZipStrategy et RarStrategy et qui n'affecterait en rien la stratégie TarGunzipStrategy. Le langage utilisé pour les exemples est Java pour sa simplicité de réalisation de classes abstraites et de compréhension. Si vous désirez voir employé un autre langage (C++, Ruby...), n'hésitez pas à nous le faire savoir.


 Listings

Listing 1

public abstract class InflateStrategy
{
  public String getName()
  {
    return "Compresseur quelconque."
  }

  public abstract void inflate(String  file , InputStream in, OutputStream out)
  throws IOException;
}
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Listing 2

public class ZipStrategy
{
  public String getName() { return "ZIP"; }
  public inflate(String file, InputStream in, OutputStream out)
  throws IOException
  {
    ZipOutputStream zout = new ZipOutputStream(out);
    zout.putNextEntry(new ZipEntry((new File(file)).getName()));
    byte c;
    while ((c = in.read()) != -1)
      zout.write(c);
    zout.closeEntry();
    zout.close();
  }
}
       
      
JextCopier dans Jext
Listing 3

public class InflateContext
{
  private InflateStrategy strategy;
  InputStream in;
  OutputStream out;
  String fileName;

  public InflateContext()
  {
    setZipStrategy();
  }

  public void setZipStrategy()
  {
    strategy = new ZipStrategy();
  }

  public void setTarGunzipStrategy()
  {
    strategy = new TarGunzipStrategy();
  }

  public void inflate()
  {
    strategy.inflate(fileName, in, out);
  }
}
       
      
JextCopier dans Jext


par Romain Guy
romain.guy@jext.org
http://www.jext.org
Dernière mise à jour : 14/10/2006



 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE