J'étudiais le modèle de décorateur tel que documenté dans GOF .
S'il vous plaît, aidez-moi à comprendre le modèle de décorateur . Quelqu'un pourrait-il donner un exemple de cas d'utilisation où cela est utile dans le monde réel?
J'étudiais le modèle de décorateur tel que documenté dans GOF .
S'il vous plaît, aidez-moi à comprendre le modèle de décorateur . Quelqu'un pourrait-il donner un exemple de cas d'utilisation où cela est utile dans le monde réel?
Réponses:
Le modèle de décorateur atteint un seul objectif d'ajouter dynamiquement des responsabilités à n'importe quel objet.
Prenons le cas d'une pizzeria. Dans la pizzeria, ils vendront quelques variétés de pizzas et ils fourniront également des garnitures dans le menu. Imaginez maintenant une situation dans laquelle si la pizzeria doit fournir des prix pour chaque combinaison de pizza et de garniture. Même s'il y a quatre pizzas de base et 8 garnitures différentes, l'application deviendrait folle en conservant toutes ces combinaisons concrètes de pizzas et de garnitures.
Voici le motif du décorateur.
Selon le modèle du décorateur, vous mettrez en œuvre des garnitures au fur et à mesure que les décorateurs et les pizzas seront décorées par les décorateurs de ces garnitures. Pratiquement chaque client voudrait des garnitures de son désir et le montant final de la facture sera composé des pizzas de base et des garnitures commandées en plus. Chaque décorateur de garniture connaîtrait les pizzas qu'il décore et son prix. La méthode GetPrice () de l'objet Topping renverrait le prix cumulé de la pizza et de la garniture.
Voici un exemple de code d'explication ci-dessus.
public abstract class BasePizza
{
protected double myPrice;
public virtual double GetPrice()
{
return this.myPrice;
}
}
public abstract class ToppingsDecorator : BasePizza
{
protected BasePizza pizza;
public ToppingsDecorator(BasePizza pizzaToDecorate)
{
this.pizza = pizzaToDecorate;
}
public override double GetPrice()
{
return (this.pizza.GetPrice() + this.myPrice);
}
}
class Program
{
[STAThread]
static void Main()
{
//Client-code
Margherita pizza = new Margherita();
Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());
ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());
MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());
JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());
Console.ReadLine();
}
}
public class Margherita : BasePizza
{
public Margherita()
{
this.myPrice = 6.99;
}
}
public class Gourmet : BasePizza
{
public Gourmet()
{
this.myPrice = 7.49;
}
}
public class ExtraCheeseTopping : ToppingsDecorator
{
public ExtraCheeseTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 0.99;
}
}
public class MushroomTopping : ToppingsDecorator
{
public MushroomTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}
public class JalapenoTopping : ToppingsDecorator
{
public JalapenoTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}
Il s'agit d'un exemple simple d'ajout dynamique d'un nouveau comportement à un objet existant ou au modèle Decorator. En raison de la nature des langages dynamiques tels que Javascript, ce modèle devient une partie du langage lui-même.
// Person object that we will be decorating with logging capability
var person = {
name: "Foo",
city: "Bar"
};
// Function that serves as a decorator and dynamically adds the log method to a given object
function MakeLoggable(object) {
object.log = function(property) {
console.log(this[property]);
}
}
// Person is given the dynamic responsibility here
MakeLoggable(person);
// Using the newly added functionality
person.log('name');
switch
ou d'un simple if
, vous pourrez affirmer qu'il s'agit d'un excellent exemple d'ajout dynamique de comportement à une classe.Mais, nous avons besoin d'au moins deux classes pour définir un décorateur et des objets décorés dans ce modèle.
Il convient de noter que le modèle d'E / S Java est basé sur le modèle de décorateur. La superposition de ce lecteur au-dessus de ce lecteur au-dessus de ... est un exemple vraiment réel de décorateur.
Exemple - Scénario - Disons que vous écrivez un module de chiffrement. Ce cryptage peut crypter le fichier clair à l'aide de la norme de cryptage DES - Data. De même, dans un système, vous pouvez avoir le cryptage en tant que norme de cryptage AES - Advance. En outre, vous pouvez avoir la combinaison de cryptage - d'abord DES, puis AES. Ou vous pouvez avoir d'abord AES, puis DES.
Discussion - Comment allez-vous gérer cette situation? Vous ne pouvez pas continuer à créer l'objet de telles combinaisons - par exemple - AES et DES - total de 4 combinaisons. Ainsi, vous devez avoir 4 objets individuels. Cela deviendra complexe à mesure que le type de cryptage augmentera.
Solution - Continuez à construire la pile - des combinaisons en fonction des besoins - au moment de l'exécution. Un autre avantage de cette approche par pile est que vous pouvez la dérouler facilement.
Voici la solution - en C ++.
Tout d'abord, vous avez besoin d'une classe de base - une unité fondamentale de la pile. Vous pouvez penser comme la base de la pile. Dans cet exemple, il s'agit d'un fichier clair. Suivons toujours le polymorphisme. Faites d'abord une classe d'interface de cette unité fondamentale. De cette façon, vous pouvez l'implémenter comme vous le souhaitez. De plus, vous n'avez pas besoin de penser à la dépendance tout en incluant cette unité fondamentale.
Voici la classe d'interface -
class IclearData
{
public:
virtual std::string getData() = 0;
virtual ~IclearData() = 0;
};
IclearData::~IclearData()
{
std::cout<<"Destructor called of IclearData"<<std::endl;
}
Maintenant, implémentez cette classe d'interface -
class clearData:public IclearData
{
private:
std::string m_data;
clearData();
void setData(std::string data)
{
m_data = data;
}
public:
std::string getData()
{
return m_data;
}
clearData(std::string data)
{
setData(data);
}
~clearData()
{
std::cout<<"Destructor of clear Data Invoked"<<std::endl;
}
};
Maintenant, créons une classe abstraite de décorateur - qui peut être étendue pour créer n'importe quel type de saveurs - ici, la saveur est le type de cryptage. Cette classe abstraite de décorateur est liée à la classe de base. Ainsi, le décorateur "est une" sorte de classe d'interface. Ainsi, vous devez utiliser l'héritage.
class encryptionDecorator: public IclearData
{
protected:
IclearData *p_mclearData;
encryptionDecorator()
{
std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
}
public:
std::string getData()
{
return p_mclearData->getData();
}
encryptionDecorator(IclearData *clearData)
{
p_mclearData = clearData;
}
virtual std::string showDecryptedData() = 0;
virtual ~encryptionDecorator() = 0;
};
encryptionDecorator::~encryptionDecorator()
{
std::cout<<"Encryption Decorator Destructor called"<<std::endl;
}
Maintenant, faisons une classe de décorateur concrète - Type de chiffrement - AES -
const std::string aesEncrypt = "AES Encrypted ";
class aes: public encryptionDecorator
{
private:
std::string m_aesData;
aes();
public:
aes(IclearData *pClearData): m_aesData(aesEncrypt)
{
p_mclearData = pClearData;
m_aesData.append(p_mclearData->getData());
}
std::string getData()
{
return m_aesData;
}
std::string showDecryptedData(void)
{
m_aesData.erase(0,m_aesData.length());
return m_aesData;
}
};
Maintenant, disons que le type de décorateur est DES -
const std :: string desEncrypt = "DES Encrypted";
class des: public encryptionDecorator
{
private:
std::string m_desData;
des();
public:
des(IclearData *pClearData): m_desData(desEncrypt)
{
p_mclearData = pClearData;
m_desData.append(p_mclearData->getData());
}
std::string getData(void)
{
return m_desData;
}
std::string showDecryptedData(void)
{
m_desData.erase(0,desEncrypt.length());
return m_desData;
}
};
Créons un code client pour utiliser cette classe de décorateur -
int main()
{
IclearData *pData = new clearData("HELLO_CLEAR_DATA");
std::cout<<pData->getData()<<std::endl;
encryptionDecorator *pAesData = new aes(pData);
std::cout<<pAesData->getData()<<std::endl;
encryptionDecorator *pDesData = new des(pAesData);
std::cout<<pDesData->getData()<<std::endl;
/** unwind the decorator stack ***/
std::cout<<pDesData->showDecryptedData()<<std::endl;
delete pDesData;
delete pAesData;
delete pData;
return 0;
}
Vous verrez les résultats suivants -
HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
DES Encrypted AES Encrypted HELLO_CLEAR_DATA
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Destructor called
Destructor called of IclearData
Encryption Decorator Destructor called
Destructor called of IclearData
Destructor of clear Data Invoked
Destructor called of IclearData
Voici le diagramme UML - Représentation de classe de celui-ci. Dans le cas où vous souhaitez ignorer le code et vous concentrer sur l'aspect de conception.
strategy pattern
?
Le modèle Decorator vous aide à modifier ou configurer une fonctionnalité de votre objet en enchaînant avec d'autres sous-classes similaires de cet objet.
Le meilleur exemple serait les classes InputStream et OutputStream dans le package java.io
File file=new File("target","test.txt");
FileOutputStream fos=new FileOutputStream(file);
BufferedOutputStream bos=new BufferedOutputStream(fos);
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.write(5);
oos.writeBoolean(true);
oos.writeBytes("decorator pattern was here.");
//... then close the streams of course.
Qu'est-ce que Decorator Design Pattern en Java.
La définition formelle du modèle Decorator du livre du GoF (Design Patterns: Elements of Reusable Object-Oriented Software, 1995, Pearson Education, Inc. Publishing as Pearson Addison Wesley) indique que vous pouvez,
"Attachez dynamiquement des responsabilités supplémentaires à un objet. Les décorateurs offrent une alternative flexible au sous-classement pour étendre les fonctionnalités."
Disons que nous avons une pizza et que nous voulons la décorer avec des garnitures telles que le poulet masala, l'oignon et le fromage mozzarella. Voyons comment l'implémenter en Java ...
Programme pour montrer comment implémenter Decorator Design Pattern en Java.
Pizza.java:
<!-- language-all: lang-html -->
package com.hubberspot.designpattern.structural.decorator;
public class Pizza {
public Pizza() {
}
public String description(){
return "Pizza";
}
}
package com.hubberspot.designpattern.structural.decorator;
public abstract class PizzaToppings extends Pizza {
public abstract String description();
}
package com.hubberspot.designpattern.structural.decorator;
public class ChickenMasala extends PizzaToppings {
private Pizza pizza;
public ChickenMasala(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + " with chicken masala, ";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class MozzarellaCheese extends PizzaToppings {
private Pizza pizza;
public MozzarellaCheese(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + "and mozzarella cheese.";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class Onion extends PizzaToppings {
private Pizza pizza;
public Onion(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + "onions, ";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class TestDecorator {
public static void main(String[] args) {
Pizza pizza = new Pizza();
pizza = new ChickenMasala(pizza);
pizza = new Onion(pizza);
pizza = new MozzarellaCheese(pizza);
System.out.println("You're getting " + pizza.description());
}
}
J'ai beaucoup utilisé le motif Decorator dans mon travail. J'ai publié un article sur mon blog sur la façon de l'utiliser avec la journalisation.
Le modèle décorateur vous permet d'ajouter dynamiquement un comportement aux objets.
Prenons un exemple où vous devez créer une application qui calcule le prix de différents types de hamburgers. Vous devez gérer différentes variantes de hamburgers, tels que «gros» ou «avec du fromage», dont chacun a un prix par rapport au hamburger de base. Par exemple, ajoutez 10 $ pour un hamburger avec du fromage, ajoutez 15 $ de plus pour un gros hamburger, etc.
Dans ce cas, vous pourriez être tenté de créer des sous-classes pour les gérer. Nous pourrions exprimer cela en Ruby comme suit:
class Burger
def price
50
end
end
class BurgerWithCheese < Burger
def price
super + 15
end
end
Dans l'exemple ci-dessus, la classe BurgerWithCheese hérite de Burger et remplace la méthode de prix pour ajouter 15 $ au prix défini dans la super classe. Vous créeriez également une classe LargeBurger et définiriez le prix par rapport à Burger. Mais vous devez également définir une nouvelle classe pour la combinaison de «gros» et «avec fromage».
Maintenant, que se passe-t-il si nous devons servir un "hamburger avec frites"? Nous avons déjà 4 classes pour gérer ces combinaisons, et nous devrons en ajouter 4 supplémentaires pour gérer toutes les combinaisons des 3 propriétés - «grande», «avec fromage» et «avec frites». Nous avons besoin de 8 classes maintenant. Ajoutez une autre propriété et nous aurons besoin de 16. Cela deviendra 2 ^ n.
Essayons plutôt de définir un BurgerDecorator qui prend en charge un objet Burger:
class BurgerDecorator
def initialize(burger)
self.burger = burger
end
end
class BurgerWithCheese < BurgerDecorator
def price
self.burger.price + 15
end
end
burger = Burger.new
cheese_burger = BurgerWithCheese.new(burger)
cheese_burger.price # => 65
Dans l'exemple ci-dessus, nous avons créé une classe BurgerDecorator, dont la classe BurgerWithCheese hérite. Nous pouvons également représenter la «grande» variation en créant la classe LargeBurger. Maintenant, nous pourrions définir un gros hamburger avec du fromage à l'exécution comme:
b = LargeBurger.new(cheese_burger)
b.price # => 50 + 15 + 20 = 85
Rappelez-vous comment l'utilisation de l'héritage pour ajouter la variante "avec frites" impliquerait l'ajout de 4 sous-classes supplémentaires? Avec les décorateurs, nous créerions simplement une nouvelle classe, BurgerWithFries, pour gérer la nouvelle variante et gérer cela au moment de l'exécution. Chaque nouvelle propriété aurait besoin d'un peu plus de décorateur pour couvrir toutes les permutations.
PS. Ceci est la version courte d'un article que j'ai écrit sur l' utilisation du modèle de décorateur en Ruby , que vous pouvez lire si vous souhaitez découvrir des exemples plus détaillés.
Décorateur:
Se référer à la fabrication de sources article pour plus de détails.
Decorator (Abstract) : c'est une classe / interface abstraite, qui implémente l'interface du composant. Il contient l'interface des composants. En l'absence de cette classe, vous avez besoin de nombreuses sous-classes de ConcreteDecorators pour différentes combinaisons. La composition du composant réduit les sous-classes inutiles.
Exemple JDK:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
char c = (char)bis.read();
System.out.println("Char: "+c);;
}
Jetez un œil à la question SE ci-dessous pour des exemples de diagramme et de code UML.
Articles utiles:
Exemple de mot réel de modèle de décorateur: VendingMachineDecorator a été expliqué @
Quand utiliser le motif décorateur?
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
Dans l'exemple ci-dessus, le thé ou le café (boisson) a été décoré par Sugar and Lemon.
Le modèle de décorateur atteint un seul objectif d' ajouter dynamiquement des responsabilités à n'importe quel objet .
Le modèle d'E / S Java est basé sur un modèle de décorateur.
Il existe un exemple sur Wikipedia sur la décoration d'une fenêtre avec une barre de défilement:
http://en.wikipedia.org/wiki/Decorator_pattern
Voici un autre exemple très «réel» de «membre d'équipe, chef d'équipe et gestionnaire», qui illustre que le modèle de décorateur est irremplaçable avec un simple héritage:
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
Il y a quelque temps, j'avais refactoré une base de code en utilisant le modèle Decorator, donc je vais essayer d'expliquer le cas d'utilisation.
Supposons que nous ayons un ensemble de services et que l'utilisateur ait acquis ou non une licence d'un service particulier, nous devons démarrer le service.
Tous les services ont une interface commune
interface Service {
String serviceId();
void init() throws Exception;
void start() throws Exception;
void stop() throws Exception;
}
abstract class ServiceSupport implements Service {
public ServiceSupport(String serviceId, LicenseManager licenseManager) {
// assign instance variables
}
@Override
public void init() throws Exception {
if (!licenseManager.isLicenseValid(serviceId)) {
throw new Exception("License not valid for service");
}
// Service initialization logic
}
}
Si vous observez attentivement, ServiceSupport
dépend de LicenseManager
. Mais pourquoi devrait-il en dépendre LicenseManager
? Et si nous avions besoin d'un service d'arrière-plan qui n'a pas besoin de vérifier les informations de licence. Dans la situation actuelle, nous devrons en quelque sorte nous entraîner LicenseManager
pour revenirtrue
aux services d'arrière-plan. Cette approche ne me paraissait pas bien. Selon moi, le contrôle de licence et les autres logiques étaient orthogonaux les uns par rapport aux autres.
Donc, modèle de décorateur vient à la rescousse et commence ici la refactorisation avec TDD.
class LicensedService implements Service {
private Service service;
public LicensedService(LicenseManager licenseManager, Service service) {
this.service = service;
}
@Override
public void init() {
if (!licenseManager.isLicenseValid(service.serviceId())) {
throw new Exception("License is invalid for service " + service.serviceId());
}
// Delegate init to decorated service
service.init();
}
// override other methods according to requirement
}
// Not concerned with licensing any more :)
abstract class ServiceSupport implements Service {
public ServiceSupport(String serviceId) {
// assign variables
}
@Override
public void init() {
// Service initialization logic
}
}
// The services which need license protection can be decorated with a Licensed service
Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
// Services which don't need license can be created without one and there is no need to pass license related information
Service aBackgroundService = new BackgroundService1("BG-1");
Prenons l'exemple de PubG. Les fusils d'assaut fonctionnent mieux avec un zoom 4x et pendant que nous y sommes, nous aurions également besoin d'un compensateur et d'un suppresseur. Cela réduira le recul et réduira le son de tir ainsi que l'écho. Nous devrons implémenter cette fonctionnalité qui permettra aux joueurs d'acheter leur arme préférée et leurs accessoires. Les joueurs peuvent acheter le pistolet ou une partie de l'accessoire ou tout l'accessoire et ils seraient facturés en conséquence.
Voyons comment le motif décorateur est appliqué ici:
Supposons que quelqu'un veuille acheter SCAR-L avec les trois accessoires mentionnés ci-dessus.
Cela conduira à un diagramme de classes comme celui-ci:
Maintenant, nous pouvons avoir des classes comme celle-ci:
public abstract class Gun {
private Double cost;
public Double getCost() {
return cost;
}
}
public abstract class GunAccessories extends Gun { }
public class Scarl extends Gun {
public Scarl() {
cost = 100;
}
}
public class Suppressor extends GunAccessories {
Gun gun;
public Suppressor(Gun gun) {
cost = 5;
this.gun = gun;
}
public double getCost(){
return cost + gun.getCost();
}
}
public class GunShop{
public static void main(String args[]){
Gun scarl = new Scarl();
scarl = new Supressor(scarl);
System.out.println("Price is "+scarl.getCost());
}
}
Nous pouvons également ajouter d'autres accessoires et décorer notre pistolet.
Référence:
https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/
Modèle de conception de décorateur : ce modèle permet de modifier les caractéristiques d'un objet lors de l'exécution. Il fournit différentes saveurs à un objet et donne la flexibilité de choisir les ingrédients que nous voulons utiliser dans cette saveur.
Exemple réel: disons que vous avez un siège principal dans la cabine d'un vol. Vous êtes désormais autorisé à choisir plusieurs équipements avec le siège. Chaque équipement a son propre coût qui lui est associé. Désormais, si un utilisateur choisit le Wifi et la nourriture premium, il / elle sera facturé pour siège + wifi + nourriture premium.
Dans ce cas, le modèle de conception de décorateur peut vraiment nous aider. Visitez le lien ci-dessus pour en savoir plus sur le modèle de décorateur et la mise en œuvre d'un exemple réel.