|
|
|
|
|
Le décorateur |
|
|
 |
|
|
|
|
|
18 Avril 2003 |
|
|
Version 1.0 |
|
|
Par Sébastien MERIC |
|
|
Remerciements : Stefan Bertholon |
|
Synonyme
Emballage
Synopsis
Ajout dynamique de responsabilités à un objet. L'ajout de responsabilités à un
objet sonne évidemment comme un héritage. C'est en effet, le moyen le plus évident
de lui fournir des responsabilités supplémentaires. Le manque de souplesse
de cette méthode (l'héritage) est bien entendu, son inconvénient majeur.
Premièrement, l'héritage n'est pas toujours possible. Deuxièmement, l'héritage
peut donner lieu à la prolifération de classes. Enfin, les responsabilités
supplémentaires ne sont pas dynamiques.
Exploration
- L'héritage n'est pas toujours possible
- Vous n'avez en effet pas toujours moyen d'accéder à la classe mère, vous ne l'avez
pas nécessairement développée vous-même et elle peut être définie comme étant finale.
Il peut aussi arriver que la classe ne soit pas finale mais qu'en hériter vous rende
nerveux à l'idée de devoir maintenir dans toutes vos classes héritières toute modification
apportée par l'éditeur de cette classe. Bref que vous ne puissiez réellement pas ou
que vous ne désiriez pas, ne change rien à votre problème : il faut éviter d'hériter
- Evite la prolifération de classes :
- Imaginons que l'on ai définie une classe dont on veut étendre les capacités. Par exemple
nous avons la classe Logger et on veut ajouter à chaque message envoyer
vers le logger, l'heure et la date d'émission. Imaginons de plus que nous voulions aussi
définir une classe pour un affichage console, un log dans un fichier, un log par email
à un administrateur un affichage SWING ! Dans chaque cas, il faut pouvoir
afficher ou non l'heure et la date. Nous voilà donc obligés de surcharger la classe logger
(pour un fichier) 3 fois pour les divers types de logger et 2 fois pour que chacun affiche
l'heure et la date, ou non.

- Les responsabilités supplémentaires ne sont pas dynamiques
- Et le dynamisme peut se représenter par plusieurs choses aussi utiles qu'esthétiques
(si tant est que le développement vous paraisse esthétique). Plusieurs points donc :
- Le dynamisme de déploiement : c'est donc un fichier de configuration qui vous permet
de choisir le mode d'utilisation de vos classes.
- Un dynamisme en runtime : à tout moment vous pouvez basculer du mode horodaté au mode
sans horodatage, du mode swing au mode console, etc.
- Enfin, et on découvre alors là une grande puissance du pattern Décorateur,
vous pouvez composer les différentes parties entre elles. Un exemple : Logger
classique (donc fichier) associé à LoggerHorodaté (donc horodatage des messages)
associés à LoggerSwing (donc affichage écran graphique) donne des résultats dans
un fichier et dans un écran, le tout horodaté. Pour faire cela avec l'héritage pur, il
m'aurait fallu hériter par exemple de LoggerSwingHorodaté et ajouter les
responsabilités pourtant déjà implémentées de log dans un fichier ! Soit encore une classe.
- Enfin, dynamisme de votre application car l'ajout d'une nouvelle extenssion devient
simple, il suffit d'ajouter une classe tandis que dans la présentation précédente, vous
ajoutez une classe multipliée par le nombre de combinaisons requises.

Structure
Sur le schéma précédent, nous pouvons étudier la structure requise pour mettre en
place le décorateur. Il nous faut une interface racine qui présente l'ensemble des
responsabilités de notre panel d'outils. Une classe d'implémentation par défaut évidemment,
c'est quand même généralement le cas et ceci permet en plus d'utiliser une
fabrique qui renvoie cette classe par défaut si rien ne lui est précisé,
ou que la configuration provoque une erreur.
Ensuite il nous faut aussi une racine pour tous les décorateurs qui maintiennent une
poignée sur un autre décorateur afin de déléguer une partie de son travail bien entendu.
Le reste du pattern, contient les classes qui implémentent cette "racine" afin d'étendre
les responsabilités.

Implémentation
Source
/*
* Logger.java
*
* Created on 10 mars 2003, 19:11
*/
package com.developpez.decorateur;
/**
*
* @author smeric
*/
public interface Logger {
/**
* Log le message.
*/
void log(java.lang.String msg);
}
Source
/*
* DefaultLogger.java
*
* Created on 10 mars 2003, 19:15
*/
package com.developpez.decorateur;
import java.io.*;
/**
* Classe d'implémentation du logger par défaut.
* Cette classe loggue les messages sans fioriture dans un flux qui peut
* être soit un fichier soit la sortie standard.
* @author smeric pour developpez.com
*/
public class DefaultLogger implements Logger {
/**
* Créer une nouvelle instance de DefaultLogger.<p>
* Cette instance a besoin de connaitre un flux pour écrire à l'intérieur.
*/
public DefaultLogger(PrintStream printSream) {
setPrintStream(writter);
}
/**
* Log le message.
*/
public void log(java.lang.String msg) {
getPrintStream().println(msg);
}
private void setPrintStream(PrintStream value) {
printStream = value;
}
private Writter getPrintStream() {
if (null == printStream) {
return System.out;
}
return printStream;
}
private PrintStream printStream;
}
Source
/*
* LoggerDecorateur.java
*
* Created on 10 mars 2003, 19:32
*/
package com.developpez.decorateur;
/**
*
* @author smeric
*/
public class LoggerDecorateur implements Logger {
/**
* Pas de constructeur par défaut, il faudra appeler celui-ci.
* Ce constructeur prend en paramètre un autre logger pour enrichir ces possibilités
* et refuse (voir le mutateur) un logger null.
*/
public LoggerDecorateur(Logger logger) {
setLogger(logger);
}
/**
* Log le message.
*/
public void log(String msg) {
getLogger().log(msg);
}
private void setLogger(Logger value) {
assert null != value : "Impossible de décorer un logger null";
logger = value;
}
private Logger logger;
}
Remarque concernant la structure
Si vous vous trouvez face à la problématique exprimée en premier point, la classe
mère n'est pas accessible pour l'héritage, il faudra implémenter la classe par défaut
sous la forme d'un adaptateur
|