Vous souhaitez étendre une classe existante dans un logiciel orienté objet avec de nouvelles fonc­tion­na­li­tés, vous avez deux options dif­fé­rentes :la solution simple, mais aussi ra­pi­de­ment dé­rou­tante, qui consiste à mettre en place des sous-classes com­plé­tant la classe de base de manière ap­pro­priée ou bien, autre solution, vous pouvez également utiliser une instance de dé­co­ra­teur selon le patron Dé­co­ra­teur. Ce modèle, qui est l’un des 23 Design Patterns GoF, permet une extension dynamique des classes pendant que le logiciel fonc­tionne. Cela élimine le besoin de hié­rar­chies d’héritage in­fi­ni­ment longues et dif­fi­ciles à com­prendre.

Ci-dessous, vous ap­pren­drez ce qu’est exac­te­ment le Decorator Pattern et quels sont ses avantages et ses in­con­vé­nients. En outre, nous il­lus­tre­rons le fonc­tion­ne­ment du modèle au moyen d’une re­pré­sen­ta­tion graphique et d’un exemple concret.

Qu’est-ce que le Decorator Pattern (patron Dé­co­ra­teur) ?

Le patron Decorator ou Decorator Pattern en anglais, est un patron de stratégie publié en 1994 pour l’extension claire des classes dans les logiciels in­for­ma­tiques orientés objet. Selon le patron, tout objet peut être étendu par un com­por­te­ment souhaité sans affecter le com­por­te­ment des autres objets de la même classe. Struc­tu­rel­le­ment, le patron Dé­co­ra­teur est très similaire au patron de con­cep­tion « Chaîne de res­pon­sa­bi­lité » mais con­trai­re­ment à ce concept de res­pon­sa­bi­lité avec un agent central, les demandes sont acceptées par toutes les classes.

Le composant logiciel à étendre est « décoré » selon le patron de con­cep­tion Dé­co­ra­teur avec une ou plusieurs classes de Decorator qui entourent com­plè­te­ment le composant. Chaque Dé­co­ra­teur est du même type que le composant joint et a donc la même interface. Cela lui permet de déléguer fa­ci­le­ment les appels de méthode entrants au composant lié, tout en exécutant éven­tuel­le­ment son propre com­por­te­ment avant ou après. Il est également possible de traiter un appel di­rec­te­ment chez le Dé­co­ra­teur.

À quoi sert le patron Dé­co­ra­teur ?

Comme d’autres modèles GoF, tels que le Strategy Pattern ou le Builder Pattern, le patron Decorator vise à rendre les com­po­sants des logiciels orientés objet plus flexibles et plus faciles à réu­ti­li­ser. À cette fin, l’approche fournit la solution pour ajouter ou supprimer des dé­pen­dances à un objet de manière dynamique et - si né­ces­saire - pendant l’exécution. Pour cette raison en par­ti­cu­lier, le modèle est une bonne al­ter­na­tive à l’uti­li­sa­tion de sous-classes : elles peuvent également compléter une classe de dif­fé­rentes manières, mais ne per­met­tent pas d’effectuer des ajus­te­ments pendant l’exécution.

Note

Un composant logiciel peut être étendu par un nombre quel­conque de classes Decorator. Pour l’accès aux instances, ces ex­ten­sions restent to­ta­le­ment in­vi­sibles, de sorte qu’elles ne re­mar­quent même pas que des classes sup­plé­men­taires précèdent la classe réelle.

Decorator Pattern : diagramme UML pour il­lus­tra­tion

Le Dé­co­ra­teur ou plutôt les classes Dé­co­ra­teur (Dé­co­ra­teur­Con­cret) ont la même interface que le composant logiciel à décorer (Com­po­sant­Con­cret) et sont du même type. Ceci est important pour le trai­te­ment des appels, qui sont transmis soit inchangés, soit modifiés si le Dé­co­ra­teur ne s’occupe pas lui-même du trai­te­ment. Dans le concept de motif Dé­co­ra­teur, cette interface élé­men­taire, qui est es­sen­tiel­le­ment une su­per­classe abstraite, est appelée « composant ».

L’in­te­rac­tion entre le composant de base et le dé­co­ra­teur peut être illustrée au mieux par une re­pré­sen­ta­tion graphique des relations sous la forme d’un diagramme de classes UML. Dans l’il­lus­tra­tion abstraite suivante du Decorator Pattern, nous avons donc utilisé le langage de mo­dé­li­sa­tion pour la pro­gram­ma­tion orientée objet.

Les avantages et les in­con­vé­nients du Decorator Pattern en un coup d’œil

La prise en compte du patron Dé­co­ra­teur lors de la con­cep­tion d’un logiciel est payante pour plusieurs raisons. Tout d’abord, il y a le haut degré de flexi­bi­lité qui vient avec une telle structure de dé­co­ra­teur : tant au moment de la com­pi­la­tion qu’à l’exécution, les classes peuvent être étendues avec de nouveaux com­por­te­ments sans héritage. Cette approche de pro­gram­ma­tion n’entraîne pas de hié­rar­chies d’héritage floues, ce qui améliore également la li­si­bi­lité du code du programme.

Le fait que la fonc­tion­na­lité soit répartie entre plusieurs classes de dé­co­ra­teurs augmente également la per­for­mance du logiciel. Ainsi, vous pouvez appeler et lancer les fonctions dont vous avez besoin pour le moment. Avec une classe de base complexe, qui fournit toutes les fonctions en per­ma­nence, cette option optimisée en termes de res­sources n’est pas dis­po­nible.

Cependant, le dé­ve­lop­pe­ment selon le patron Decorator n’a pas que des avantages : avec l’in­tro­duc­tion du modèle, la com­plexité du logiciel augmente au­to­ma­ti­que­ment. L’interface de Decorator en par­ti­cu­lier est gé­né­ra­le­ment très verbeuse et associée à de nombreux nouveaux termes, et donc tout sauf facile d’accès pour les débutants. Un autre in­con­vé­nient est le grand nombre d’objets Decorator, pour lesquels une sys­té­ma­ti­sa­tion séparée est re­com­man­dée afin d’éviter d’être confronté à des problèmes de vue d’ensemble, si­mi­laires à ceux ren­con­trés lors du travail avec des sous-classes. Les chaînes d’appel souvent très longues des objets décorés (c’est-à-dire les com­po­sants logiciels étendus) rendent également plus difficile la recherche d’erreurs et donc le processus de débogage en général.

Avantages In­con­vé­nients
Un degré élevé de flexi­bi­lité Grande com­plexité du logiciel (en par­ti­cu­lier l’interface Decorator)
Extension des fonctions des classes sans héritage Ne convient pas aux débutants
Un code de programme bien lisible Nombre élevé d’objets
Appels de fonction optimisés pour les res­sources Processus de débogage difficile

Patron Dé­co­ra­teur : scénarios d’ap­pli­ca­tion typiques

Le Decorator Pattern constitue la base des objets dy­na­miques et trans­pa­rents d’un logiciel. Les com­po­sants d’in­ter­faces uti­li­sa­teur gra­phiques sont un domaine d’ap­pli­ca­tion typique du patron : si, par exemple, un champ de texte doit être pourvu d’une bordure, un dé­co­ra­teur cor­res­pon­dant qui est « in­vi­si­ble­ment » commuté entre l’objet champ de texte et l’appel est suffisant pour insérer ce nouvel élément d’interface.

Un exemple très connu de la mise en œuvre du patron de con­cep­tion Dé­co­ra­teur est celui des classes de flux de la bi­blio­thèque Java, qui sont res­pon­sables du trai­te­ment des données d’entrée et de sortie. Les classes de dé­co­ra­teurs sont utilisées ici notamment pour ajouter de nouvelles pro­prié­tés et in­for­ma­tions de statut au flux de données ou pour fournir de nouvelles in­ter­faces.

Bien entendu, Java n’est pas le seul langage de pro­gram­ma­tion dans lequel l’uti­li­sa­tion du patron Dé­co­ra­teur est courante. Les langages suivants s’appuient également sur le patron Decorator :

  • C++
  • C#
  • Go
  • Ja­vaS­cript
  • Python
  • PHP

Exemple pratique pour la mise en œuvre du Decorator Pattern

La liste des avantages et des in­con­vé­nients montre que le patron Dé­co­ra­teur ne convient pas à tous les types de logiciels. Cependant, lorsqu’une classe doit être changée par la suite, et surtout dans les projets où cela ne peut se faire en utilisant des sous-classes, le modèle de con­cep­tion est une solution de premier ordre.

Le point de départ dans ce cas est un logiciel qui rend les noms des personnes ac­ces­sibles via la classe abstraite « employé ». Cependant, la première lettre des noms retrouvés est toujours en minuscule. Comme une adap­ta­tion ul­té­rieure est im­pos­sible, la classe de dé­co­ra­teur « Em­ployé­De­co­ra­tor » est im­plé­men­tée, qui fonc­tionne via la même interface et permet également l’appel de la méthode getName(). En outre, le dé­co­ra­teur reçoit une logique qui garantit que la première lettre est cor­rec­te­ment mise en majuscule. L’exemple de code approprié ressemble à ceci :

public class EmployéDecorator implements Person {
private Employé employé;
public EmployéDecorator(Employé employé){
	this.employé = employé;
}
public String getName(){
	// appelle la méthode de la classe d’employés
	String name = employé.getName();
	// Veillez à ce que la première lettre soit en majuscule ici
	name = Character.toUpperCase(name.charAt(0)) 
	+ name.substring(1, name.length());
	return name;
}
}
Aller au menu principal