samedi 13 mai 2006

Les patrons


LES PATRONS

Il arrive régulièrement dans un domaine qu'il y ait des problèmes récurrents. L'informatique ne fait pas exception. Les programmeurs doivent régulièrement résoudre les mêmes problèmes.

Les patrons tentent de corriger cette lacune. Les patrons ressoudent de façon globale ces problèmes classiques. Les patrons ne sont pas liés à une implémentation. L'utilisation de cet outil permet d'avoir une solution qui a été éprouvée. Ils font parties des concepts de bonne pratique prôné par le génie logiciel. Il permet de résoudre un problème qui a été résolu maintes fois. Plusieurs solutions peuvent exister, mais certaines peuvent être plus adéquates que d'autres. Les utiliser permet d'économiser du temps et d'avoir une solution rapidement. À long terme, on pourrait dire que le but serait d'avoir un "handbook" tel qu'on en retrouve en mécanique et électronique.

Documentation

Il y a de plus en plus de patron qui est créé. La création d'une documentation pour un patron permet de partager nos connaissances et d'offrir une solution à plusieurs personnes qui pourront à leurs tours l'utiliser. Il est ainsi possible de déceler les problèmes s'il y en a et d'améliorer la solution.
La documentions d'un patron suit une syntaxe particulière.
  • Nom du patron, il doit être signification
  • Une description du problème que le patron tente de résoudre
  • Le contexte du patron.
  • Forces, une liste des points négatifs et positifs du patron
  • Une solution possible pour résoudre le problème
  • On crée un exemple

Divers patrons

Singleton

Description

Le Singleton s'assure qu'une seule instance de la classe est créée.

Contexte

Il y a plusieurs occasions où une seule instance d'un objet doit exister. Ce patron s'applique dans les situations où le nombre d'instances d'un programme doit être unique. Il n'est donc pas possible d'ouvrir plusieurs fois le même programme. Imaginez que vous écrivez une classe qui permet d'écouter un mp3. Si deux instances fonctionnent simultanément, l'utilisateur n'entendra rien. La classe écrite devrait terminer le flux audio courant et démarrer le nouveau.

Forces

Il n'y a qu'une instance de créée. Cette instance est accessible à tous ses clients et aux autres classes qui accèdent à elle. Il y a un gain au niveau de la mémoire.

Solutions

Il y a de nombreuse façon possible de s'assurer qu'une seule instance existe. Un moyen simple serait d'employer une variable globale, mais on peut rapidement rendre le programme illisible et difficile à maintenir. Une meilleure approche consiste à donner la responsabilité à la classe. La classe peut ainsi être en mesure de créer une instance seulement si aucune autre n’existe. Il est aussi possible de créer une fonction recevant une classe en paramètre et de vérifier à l'aide d'une variable si une autre instance existe.

Exemple

Le premier exemple utilisera une fonction afin de restreindre la classe à une seule instance. Cette fonction est codée en php.

function staticInstance($class) {
    static $instance; 
    if(!isset($instance)) { 
        $instance =& new $class; 
    }
    return($instance); 
}

La fonction reçoit une classe en paramètre, une variable statique est créée, si l'instance
n'existe pas on crée une instance de la classe.
Dans cet autre exemple, c'est la classe elle-même qui vérifie si une instance existe déjà.

public class Singleton
{
  private Singleton() {}

  static private Singleton instance;

  static public Singleton getInstance() {
    if (instance == null) 
      instance = new Singleton();
    return _instance;
  }
}

Le constructeur de cette classe est privé afin d'empêcher l'instanciation en dehors de la classe. Cette variable
de classe ne peut donc pas être accédé et modifié par aucune autre classe. Une instance de Singleton
sera créée lors du premier appel de getInstance. Instance aura donc une valeur nulle. 
S'il y a d'autres appels, la valeur ne sera pas nulle et une référence sur l'objet Singleton existant
sera retournée.

Façade

Description

Cache l'accès de nombreux objet derrière une classe

Contexte

Une classe x peut avoir de nombreuses relations avec d'autres classes. Il est alors fastidieux d'employer cette classe x, car cette classe doit en connaître plusieurs autres. La classe x devient alors plus difficile à réutiliser. Le patron façade ajoute une barrière entre la classe de base et les autres. La complexité est ainsi masquée.

Force

Il y a simplification de la classe client, déplace la complexité vers la classe façade.

Solution

L'objet façade permet l'accès direct à d'autres objets qui fournit la fonctionnalité désirée via une méthode.

Object pool

Description

Gérer la réutilisation des objets

Contexte

Il arrive souvent que plusieurs clients utilisent les mêmes ressources. Il peut être intéressant de limiter le nombre d'accès à ces ressources afin d'obtenir de bonnes performances. Un serveur Web qui fait des accès à une base de données peut-être rapidement mis à bout de souffle si toutes les connexions demandées sont acceptées. Chaque connexion à la base de donnée prend du temps et il y a dégradation des performances à chaque connexion d'ajoutée. Il est souvent possible d'utiliser les instances existantes.

Forces

Limiter le nombre d'instances créé, donc limiter les ressources utilisées. Éviter de créer des ressources afin d'utiliser ce qui existe déjà.

Solution

La réutilisation d'instance est privilégié. Il est possible d'utiliser un tableau lorsqu'on limite le nombre d'instances. L'objet qui crée l'instance doit être celui qui compte aussi les instances créées. Il y a donc un gestionnaire. La façon de prendre un objet de la pool et d'y retourner un objet est importante.

Exemple

Lors de de la première initialisation du ArrayPoolManager, un tableau est créé pour y entreposer les objets. Lors d'une insertion ou suppression, la quantité d'objets est mise à jour. La méthode getObject permet d'obtenir un objet dans la liste. L'objet est retourné. La méthode putObject remet un objet dans la pool. Cette méthode est employée lorsqu'on n'a plus besoin de l'objet. Le code source a été pris du site : microjava.
/**
    * Tableau qui contient les objets prêt à être utilisé
    */
   private Object[]  m_Pool;
    /**
    * La taille maximale du tableau
    */
   private int       m_Capacity;
   /**
    * Le prochain projet disponible, -1 signifie qu'il y a aucun objet.
    */
   private int       m_NextAvail = -1;
 
   public ArrayPoolManager(int capacity)
   {
      m_Capacity = capacity;
      m_Pool = new Object[m_Capacity];
   }


   public Object getObject()
   {
      /* Vérifie si un objet est disponible pour être retourné */
      if (m_NextAvail >= 0)
      {
         /*Libère l'objet du tableau, seul l'appelant à une référence à l'objet*/

         Object t = m_Pool[m_NextAvail];
         m_Pool[m_NextAvail--] = null;
         return t;
      }
 
      return null;
   }
   
   public void putObject(Object obj)
   {
      if (m_NextAvail < m_Capacity)
      {
         m_Pool[++m_NextAvail] = obj;
      }
   }
Les gains s'améliorent à mesure: que le nombre d'objets nécessaires s'accroît et que la complexité des objets est grande.

Délégation

Description

donnée des responsabilités d'une classe à une autre.

Contexte

Il arrive fréquemment qu'on utilise l'héritage afin d'obtenir les méthodes et attribut d'une classe. C'est un des concepts clés de la programmation orientée objet. En utilisant à outrance cette technique, des problèmes sur le plan des performances peuvent survenir.

Forces

Limiter le couplage, mais augmenter la cohésion. L'héritage est moins utilisé. Le code est donc plus simple et les mises à jour sont plus aisées à effectuer. Le code peut par contre être moins clair.

Solutions

Via une instance de classe dans un objet, on accède à ces méthodes.

Exemple

On établie un lien des fonctions désirées à l'objet «delageted». L'objet «delegate» implémente les fonctions voulues qui sont liées à une fonction de l'objet «delageted».


class Delegated
{
    public int fonction1(String args)
    {

    }
}

class Delegate
{
    protected delegated = new Delegated();

    public int fonction1(String args)
    {
         //On appele l'objet Delegated
         return delegated.fonction1(args);
    }
} 

Quelques patrons ont été abordés dans cet article. Il en existe plusieurs autres. Documentez-vous sur le sujet lors de la création d'un système. De cette façon, vous pouvez résoudre vos problèmes de façon efficace