Des lecteurs comme vous aident à soutenir MUO. Lorsque vous effectuez un achat en utilisant des liens sur notre site, nous pouvons gagner une commission d'affiliation. En savoir plus.

L'un des principes les plus importants dans le développement de logiciels est le principe de conception ouvert-fermé. Ce principe de conception souligne que les classes doivent être ouvertes pour l'extension, mais fermées pour la modification. Le motif de conception du décorateur incarne le principe de conception ouvert-fermé.

Avec le modèle de conception de décorateur, vous pouvez facilement étendre une classe en lui donnant un nouveau comportement sans modifier son code existant. Le modèle de décorateur le fait dynamiquement au moment de l'exécution, en utilisant la composition. Ce modèle de conception est connu comme une alternative flexible à l'utilisation de l'héritage pour étendre le comportement.

Comment fonctionne le motif de conception du décorateur ?

Bien que le motif décorateur soit une alternative à

héritage de classe, il intègre certains aspects de l'héritage dans sa conception. Un aspect clé du modèle de décorateur est que toutes ses classes sont liées, directement ou indirectement.

Un modèle de conception de décorateur typique a la structure suivante :

Dans le diagramme de classes ci-dessus, vous pouvez voir que le modèle de décorateur a quatre classes principales.

Composant: c'est une classe abstraite (ou interface), qui sert de supertype pour le modèle de décorateur.

Composant Béton : ce sont les objets que vous pouvez décorer avec différents comportements lors de l'exécution. Ils héritent de l'interface du composant et implémentent ses fonctions abstraites.

Décorateur: cette classe est abstraite et a le même supertype que l'objet qu'elle va décorer. Dans le diagramme de classes, vous verrez deux relations entre les classes de composants et de décorateurs. La première relation est une relation d'héritage; chaque décorateur est un composant. La seconde relation est une relation de composition; chaque décorateur a un (ou enveloppe un) composant.

BétonDécorateur : ce sont les décorateurs individuels qui donnent à un composant un comportement spécifique. Vous devez noter que chaque décorateur concret a une variable d'instance qui contient une référence à un composant.

Implémentation du modèle de conception de décorateur en Java

Un exemple d'application de commande de pizza peut démontrer de manière adéquate comment utiliser le modèle de décorateur pour développer des applications. Cet exemple d'application de pizza permet aux clients de commander des pizzas avec plusieurs garnitures. La première classe du pattern décorateur est l'interface pizza :

publicinterfacePizza{
publicabstrait Chaîne description();
publicabstraitdoublecoût();
}

L'interface Pizza est la classe du composant. Ainsi, vous pouvez en créer une ou plusieurs classes concrètes. L'entreprise de pizza fabrique deux principaux types de pizzas, en fonction de leur pâte. Un type de pizza a une pâte à levure :

publicclasseLevureCroûtePizzamet en oeuvrePizza{
@Passer outre
public Chaîne description(){
retour"Pâte à pizza à base de levure";
}

@Passer outre
publicdoublecoût(){
retour18.00;
}
}

Le YeastCrustPizza est le premier béton Classe Java de l'interface Pizza. L'autre type de pizza disponible est le pain plat :

publicclassePain PlatCroûtePizzamet en oeuvrePizza{
@Passer outre
public Chaîne description(){
retour"Pâte à pizza faite avec du pain plat";
}

@Passer outre
publicdoublecoût(){
retour15.00;
}
}

La classe FlatbreadCrustPizza est le deuxième composant concret et, comme la classe YeastCrustPizza, elle implémente toutes les fonctions abstraites de l'interface Pizza.

Les Décorateurs

La classe décorateur est toujours abstraite, vous ne pouvez donc pas créer une nouvelle instance directement à partir de celle-ci. Mais il est nécessaire d'établir une relation entre les différents décorateurs et les composants qu'ils décoreront.

publicabstraitclasseGarnitureDécorateurmet en oeuvrePizza{
public Chaîne description(){
retour"Garniture inconnue";
}
}

La classe ToppingDecorator représente la classe de décorateur dans cet exemple d'application. Désormais, l'entreprise de pizzas peut créer de nombreuses garnitures (ou décorateurs) différentes à l'aide de la classe ToppingDecorator. Disons qu'une pizza peut avoir trois types de garnitures différentes, à savoir du fromage, du pepperoni et des champignons.

Garniture au fromage

publicclasseFromages'étendGarnitureDécorateur{
privé Pizza pizza;

publicFromage(Pizza pizza){
ce.pizza = pizza;
}

@Passer outre
public Chaîne description(){
retour pizza.description() + ", garniture au fromage";
}

@Passer outre
publicdoublecoût(){
retourpizza.coût() + 2.50;
}
}

Garniture au pepperoni

publicclassePepperonis'étendGarnitureDécorateur{
privé Pizza pizza;

publicPepperoni(Pizza pizza){
ce.pizza = pizza;
}

@Passer outre
public Chaîne description(){
retour pizza.description() + ", Garniture Pepperoni";
}

@Passer outre
publicdoublecoût(){
retourpizza.coût() + 3.50;
}
}

Garniture aux champignons

publicclasseChampignons'étendGarnitureDécorateur{
privé Pizza pizza;

publicChampignon(Pizza pizza){
ce.pizza = pizza;
}

@Passer outre
public Chaîne description(){
retour pizza.description() + ", Garniture aux champignons";
}

@Passer outre
publicdoublecoût(){
retourpizza.coût() + 4.50;
}
}

Vous disposez maintenant d'une application simple implémentée à l'aide du modèle de conception de décorateur. Si un client commande une pizza à croûte de levure avec du fromage et du pepperoni, le code de test pour ce scénario se présentera comme suit :

publicclassePrincipal{
publicstatiqueannulerprincipal(Chaîne [] arguments){
Pizza pizza1 = nouveau LevureCroûtePizza();
pizza1 = nouveau Pepperoni (pizza1);
pizza1 = nouveau Fromage (pizza1);
System.out.println (pizza1.description() + " $" + pizza1.coût());
}
}

L'exécution de ce code produira le résultat suivant dans la console :

Comme vous pouvez le voir, la sortie indique le type de pizza ainsi que son coût total. La pizza a commencé comme une pizza à croûte de levure pour 18,00 $, mais avec le modèle de décorateur, l'application a pu ajouter de nouvelles fonctionnalités et leur coût approprié à la pizza. Ainsi, donner à la pizza un nouveau comportement sans altérer le code existant (la pizza à croûte de levure).

Avec le motif décorateur, vous pouvez également appliquer le même comportement à un objet autant de fois que vous le souhaitez. Si un client commande une pizza avec tout ce qu'il y a dessus et du fromage supplémentaire, vous pouvez mettre à jour la classe principale avec le code suivant pour refléter cela :

Pizza pizza2 = nouveau LevureCroûtePizza();
pizza2 = nouveau Pepperoni (pizza2);
pizza2 = nouveau Fromage (pizza2);
pizza2 = nouveau Fromage (pizza2);
pizza2 = nouveau Champignon (pizza2);

System.out.println (pizza2.description() + " $" + pizza2.coût());

L'application mise à jour produira le résultat suivant dans la console :

Les avantages de l'utilisation du modèle de conception Decorator

Les deux principaux avantages de l'utilisation du modèle de conception de décorateur sont la sécurité et la flexibilité. Le modèle de décorateur vous permet de développer un code plus sécurisé en n'interférant pas avec le code sécurisé préexistant. Au lieu de cela, il étend le code existant grâce à la composition. Empêcher efficacement l'introduction de nouveaux bogues ou d'effets secondaires imprévus.

En raison de la composition, un développeur dispose également d'une grande flexibilité lors de l'utilisation du motif de décorateur. Vous pouvez implémenter un nouveau décorateur à tout moment pour ajouter un nouveau comportement, sans modifier le code existant et sans perturber l'application.