Java Pattern Decorater - Étendez la fonctionnalité de vos objets de manière dynamique et structurée

Ce modèle est utilisé pour étendre la fonctionnalité des objets sans avoir recours à l'héritage ou à la modification du code d'origine. Il fait partie de la série "Design Pattern in Java".

Qu'est-ce qu'un décorateur ?

Un Décorateur est un modèle de conception (utilisé en programmation en général) pour étendre la fonctionnalité d'un objet en créant un objet d'emballage.
Cet objet enveloppant, appelé décorateur, est utilisé pour envelopper l'objet original qui doit être étendu.

L'objet décorateur a les mêmes fonctions que l'objet original qui est enveloppé. Mais vous pouvez également l'étendre avec des fonctions spécifiques utilisées par ce décorateur.
La fonctionnalité de l'objet décorateur est ajoutée au moment de l'exécution de votre application.

Un diagramme UML d'une application qui utilise le modèle de conception "Decorator".

L'interface "Burger": Cette interface est également appelée "Composant" et elle définit les méthodes qui seront implémentées par classe et qui seront ajoutées par la suite.
La classe "ChickenBurger": Cette classe est la mise en œuvre de la "Composante". Il peut y avoir plusieurs classes qui implémentent également la "Composante".
La classe abstraite "BurgerDecorator": C'est le décorateur qui décore notre objet Burger original et qui contient l'objet "Composant". Les objets définis dans cette instance sont accessibles aux classes enfant.
La classes "Exotic", "Mozarella" et "Vegetables": Ce sont les décorateurs de béton. Ils mettent en œuvre la fonctionnalité de décoration et peuvent être étendus avec leur fonctionnalité respective.

 

Un exemple de demande

Voici un exemple d'application appelée "TastyBurgers" qui est la réalisation du diagramme UML ci-dessus.
À l'aide de cet exemple, vous obtiendrez une compréhension plus pratique de ce modèle de conception.

 

a. Interface de la composante

public interface Burger {
	String getName();
	Integer getToppingsAmount();
}

Toutes les méthodes qui sont au moins disponibles pour un Burger sont définies ici.

 

b. Mise en œuvre du Composant

public class ChickenBurger implements Burger {	
	
	public ChickenBurger() {
	}
	
	@Override
	public String getName() {
		return "Standard Chicken Burger";
	}

	@Override
	public Integer getToppingsAmount() {
		return 0;
	}

}

 

c. Classe décorateur

public abstract class BurgerDecorator implements Burger{

	protected Burger burger;
	
	public BurgerDecorator(Burger b) {
		this.burger=b;
	}
	
	@Override
	public String getName() {
		return burger.getName();
	}

	@Override
	public Integer getToppingsAmount() {
		return burger.getToppingsAmount();
	}
	
}

Cette classe abstraite met en œuvre l'interface définie d'un Burger.

 

d. Décorateur concret

public class Exotic extends BurgerDecorator{

	public Exotic(Burger b) {
		super(b);
	}
	
	@Override
	public String getName() {
		return "Exotic Flavour Edition";
	}

	@Override
	public Integer getToppingsAmount() {
		return 5;
	}
	
	//Extended functionality - Only available in an Exotic Burger
	public Integer getSpicinessLevel() {
		return 10;
	}

}
public class Mozarella extends BurgerDecorator{
	
	public Mozarella(Burger b) {
		super(b);
	}
	
	@Override
	public String getName() {
		return "Mozarella Cheese";
	}

	@Override
	public Integer getToppingsAmount() {
		return 2;
	}

}
public class Vegetables extends BurgerDecorator {

	public Vegetables(Burger b) {
		super(b);
	}

	@Override
	public String getName() {
		return "Healthy Vegetables Edition";
	}

	@Override
	public Integer getToppingsAmount() {
		return 4;
	}
}

Les extensions de fonctionnalités sont ajoutées ici.

Les décorateurs créés peuvent être appelés dans ce format :

public class TastyBurgers {

	public static void main(String[] args) {
		Burger burgerA = new Mozarella(new ChickenBurger());
		Burger burgerB = new Exotic(new ChickenBurger());
		Burger burgerC = new Vegetables(new ChickenBurger());
		System.out.println("Burger A Name: " + burgerA.getName() + " - Toppings No.: " + burgerA.getToppingsAmount());
		System.out.println("Burger B Name: " + burgerB.getName() + " - Toppings No.: " + burgerB.getToppingsAmount());
		System.out.println("Burger C Name: " + burgerC.getName() + " - Toppings No.: " + burgerC.getToppingsAmount());
		//The function which was extended (class: Exotic) can be accessed only if you define an object directly with the class name "Exotic":
		Exotic burgerBExtended = new Exotic(new ChickenBurger());
		System.out.println("Burger B Extended - Name: " + burgerBExtended.getName() + " - Toppings No.: " + burgerBExtended.getToppingsAmount() + " - Spiciness Level: " + burgerBExtended.getSpicinessLevel());

	}
}

Résultat:

Burger A Name: Mozarella Cheese - Toppings No.: 2
Burger B Name: Exotic Flavour Edition - Toppings No.: 5
Burger C Name: Healthy Vegetables Edition - Toppings No.: 4
Burger B Extended - Name: Exotic Flavour Edition - Toppings No.: 5 - Spiciness Level: 10

Si vous souhaitez utiliser une fonctionnalité étendue dans un décorateur de béton, vous devez déclarer l'objet directement sans la classe d'interface.

Ceci est un exemple de décorateur. Si vous programmez en Java, vous trouverez régulièrement des classes Java qui utilisent le modèle de décorateur. Par exemple, BufferedInputStream ou BufferedOutputStream.

 

Cette application (projet) sur Github:

https://github.com/a-dridi/decorator-tastyburgers