Je passe en revue mes modèles de conception, et un modèle que je n'ai pas encore utilisé sérieusement dans mon codage est le modèle de décorateur.
Je comprends le motif, mais ce que j'aimerais bien savoir, ce sont quelques bons exemples concrets de fois dans le monde réel où le motif de décorateur est la solution optimale/optimale/élégante. Des situations spécifiques où la nécessité du motif de décorateur est vraiment pratique.
Merci.
Le motif décoratif est beaucoup utilisé avec les flux: vous pouvez envelopper un flux avec un flux pour bénéficier de fonctionnalités supplémentaires. J'ai vu cela avec le framework .Net - autant que je sache, cela se produit ailleurs. Mon préféré est d'utiliser GZipStream autour d'un FileStream pour une compression accrue.
Le motif Decorator est utilisé pour ajouter des fonctionnalités supplémentaires à un objet particulier, par opposition à une classe d'objets. Il est facile d'ajouter des fonctionnalités à une classe entière d'objets en sous-classant un objet, mais il est impossible d'étendre un seul objet de cette façon. Avec le motif de décorateur, vous pouvez ajouter des fonctionnalités à un seul objet et laisser les autres sans modification.
En Java, un exemple classique de modèle de décorateur est l'implémentation Java I/O Streams.
FileReader frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);
Le code précédent crée un lecteur - lrdr
- qui lit un fichier et trace les numéros de ligne. La ligne 1 crée un lecteur de fichiers (frdr
) et la ligne 2 ajoute le suivi du numéro de ligne.
En fait, je vous encourage vivement à consulter le code source Java pour les classes d'E/S Java.
J'ai récemment utilisé le modèle de décorateur dans un service Web utilisant l'interface CommandProcessor suivante:
public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);
Fondamentalement, le CommandProcessor reçoit une demande et crée la commande appropriée, l'exécute, crée la réponse appropriée et envoie la réponse. Lorsque j'ai voulu ajouter du temps et le consigner, j'ai créé un TimerDecorator qui utilisait un CommandProcessor existant en tant que composant. TimerDecorator implémente l'interface CommandProcessor, mais ajoute simplement du temps et appelle ensuite sa cible, qui est le véritable CommandProcessor. Quelque chose comme ça:
public class TimerDecorator implements CommandProcessor {
private CommandProcessor target;
private Timer timer;
public TimerDecorator(CommandProcessor processor) {
this.target = processor;
this.timer = new Timer();
}
public Command receive(Request request) {
this.timer.start();
return this.target.receive(request);
}
public Response execute(Command command) {
return this.target.execute(command);
}
public void respond(Response response) {
this.target.response(response);
this.timer.stop();
// log timer
}
}
Ainsi, le véritable CommandProcessor est intégré à TimerDecorator et je peux traiter TimerDecorator exactement comme le CommandProcessor cible, mais la logique de minutage a maintenant été ajoutée.
Le décorateur est simple mais extrêmement puissant. C'est un élément clé pour séparer les préoccupations et constitue un outil essentiel pour le principe de fermeture. Prenons un exemple courant de commande d'un produit:
IOrderGateway
{
void PlaceOrder(Order order);
{
Implémentation principale: AmazonAffiliateOrderGateway
Les décorateurs possibles pourraient être:
IncrementPerformanceCounterOrderGateway
QueueOrderForLaterOnTimeoutOrderGateway
EmailOnExceptionOrderGateway
InterceptTestOrderAndLogOrderGateway
Jetez un coup d'oeil ici pour un exemple plus détaillé.
Decorator pattern modifie de manière dynamique la fonctionnalité d'un objet au moment de l'exécution sans affecter la fonctionnalité existante de ces objets.
Cas d'utilisation clés: _
Désavantages:
Un exemple concret: Calcule le prix d'une boisson, qui peut contenir plusieurs saveurs.
abstract class Beverage {
protected String name;
protected int price;
public Beverage(){
}
public Beverage(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
protected void setPrice(int price){
this.price = price;
}
protected int getPrice(){
return price;
}
protected abstract void decorateBeverage();
}
class Tea extends Beverage{
public Tea(String name){
super(name);
setPrice(10);
}
public void decorateBeverage(){
System.out.println("Cost of:"+ name +":"+ price);
// You can add some more functionality
}
}
class Coffee extends Beverage{
public Coffee(String name){
super(name);
setPrice(15);
}
public void decorateBeverage(){
System.out.println("Cost of:"+ name +":"+ price);
// You can add some more functionality
}
}
abstract class BeverageDecorator extends Beverage {
protected Beverage beverage;
public BeverageDecorator(Beverage beverage){
this.beverage = beverage;
setName(beverage.getName()+"+"+getDecoratedName());
setPrice(beverage.getPrice()+getIncrementPrice());
}
public void decorateBeverage(){
beverage.decorateBeverage();
System.out.println("Cost of:"+getName()+":"+getPrice());
}
public abstract int getIncrementPrice();
public abstract String getDecoratedName();
}
class SugarDecorator extends BeverageDecorator{
public SugarDecorator(Beverage beverage){
super(beverage);
}
public void decorateBeverage(){
super.decorateBeverage();
decorateSugar();
}
public void decorateSugar(){
System.out.println("Added Sugar to:"+beverage.getName());
}
public int getIncrementPrice(){
return 5;
}
public String getDecoratedName(){
return "Sugar";
}
}
class LemonDecorator extends BeverageDecorator{
public LemonDecorator(Beverage beverage){
super(beverage);
}
public void decorateBeverage(){
super.decorateBeverage();
decorateLemon();
}
public void decorateLemon(){
System.out.println("Added Lemon to:"+beverage.getName());
}
public int getIncrementPrice(){
return 3;
}
public String getDecoratedName(){
return "Lemon";
}
}
public class VendingMachineDecorator {
public static void main(String args[]){
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
}
}
sortie:
Cost of:Assam Tea:10
Cost of:Assam Tea+Lemon:13
Added Lemon to:Assam Tea
Cost of:Assam Tea+Lemon+Sugar:18
Added Sugar to:Assam Tea+Lemon
Cost of:Cappuccino:15
Cost of:Cappuccino+Lemon:18
Added Lemon to:Cappuccino
Cost of:Cappuccino+Lemon+Sugar:23
Added Sugar to:Cappuccino+Lemon
Cet exemple calcule le coût d'une boisson dans un distributeur automatique après l'ajout de nombreuses saveurs à la boisson.
Dans l'exemple ci-dessus:
Coût du thé = 10, citron = 3 et sucre = 5. Si vous faites du sucre + citron + thé, cela coûte 18.
Coût du café = 15, citron = 3 et sucre = 5. Si vous faites du sucre + citron + café, cela coûte 23
En utilisant le même décorateur pour les deux boissons (thé et café), le nombre de sous-classes a été réduit. En l'absence de motif Décorateur, vous devez avoir différentes sous-classes pour différentes combinaisons.
Les combinaisons seront comme ceci:
SugarLemonTea
SugarTea
LemonTea
SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino
etc.
En utilisant la même Décorateur pour les deux boissons, le nombre de sous-classes a été réduit. C'est possible grâce au concept composition_ _ plutôt qu'à héritage utilisé dans ce modèle.
Question SE connexe:
Liens utiles:
design-patterns-decorator par dzone
décorateur par sourcemaking
oodesign article
Zend Framework utilise le décorateur pour les éléments de formulaire
Quelques informations supplémentaires: http://framework.zend.com/manual/en/zend.form.decorators.html
Le motif Decorator est utilisé par le langage C # lui-même. Il est utilisé pour décorer la classe Stream I/O de C #. Les versions décorées sont classées BufferedStream, FileStrem, MemoryStrem, NetworkStream et CryptoStream.
Ces sous-classes héritent de la classe Stream et contiennent également une instance de la classe Stream.
En savoir plus ici