Stavo studiando il motivo decorativo come documentato nel GOF .
Per favore, aiutami a capire il motivo del decoratore . Qualcuno potrebbe dare un esempio di caso d'uso di dove questo è utile nel mondo reale?
Stavo studiando il motivo decorativo come documentato nel GOF .
Per favore, aiutami a capire il motivo del decoratore . Qualcuno potrebbe dare un esempio di caso d'uso di dove questo è utile nel mondo reale?
Risposte:
Il modello Decorator raggiunge un unico obiettivo di aggiungere dinamicamente responsabilità a qualsiasi oggetto.
Prendi in considerazione un caso di una pizzeria. Nella pizzeria venderanno alcune varietà di pizza e forniranno anche condimenti nel menu. Ora immagina una situazione in cui la pizzeria deve fornire i prezzi per ogni combinazione di pizza e topping. Anche se ci sono quattro pizze di base e 8 condimenti diversi, l'applicazione impazzirebbe mantenendo tutte queste combinazioni concrete di pizze e condimenti.
Ecco che arriva il motivo del decoratore.
Come per il modello del decoratore, implementerai i condimenti poiché i decoratori e le pizze saranno decorati dai decoratori di questi condimenti. Praticamente ogni cliente vorrebbe condimenti del suo desiderio e l'importo del conto finale sarà composto dalle pizze di base e condimenti aggiuntivi ordinati. Ogni decoratore topping saprebbe delle pizze che sta decorando ed è il prezzo. Il metodo GetPrice () dell'oggetto Topping restituisce il prezzo cumulativo sia della pizza che del topping.
Ecco un esempio di codice di spiegazione sopra.
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;
}
}
Questo è un semplice esempio di aggiunta dinamica di un nuovo comportamento a un oggetto esistente o al motivo Decoratore. A causa della natura di linguaggi dinamici come Javascript, questo modello diventa parte del linguaggio stesso.
// 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
o un semplice if
, si sarebbe in grado di affermare che questo è un ottimo esempio di aggiunta dinamica di comportamento a una classe. MA, abbiamo bisogno di almeno due classi per definire un decoratore e oggetti decorati in questo modello.
Vale la pena notare che il modello di i / o Java si basa sul modello di decorazione. La stratificazione di questo lettore in cima a quel lettore in cima a ... è un esempio davvero reale di decoratore.
Esempio - Scenario- Supponiamo che tu stia scrivendo un modulo di crittografia. Questa crittografia può crittografare il file clear utilizzando DES - Standard di crittografia dei dati. Allo stesso modo, in un sistema è possibile avere la crittografia come AES - Advance standard di crittografia. Inoltre, puoi avere la combinazione di crittografia: prima DES, poi AES. Oppure puoi avere prima AES, poi DES.
Discussione: come gestirai questa situazione? Non è possibile continuare a creare l'oggetto di tali combinazioni, ad esempio AES e DES, per un totale di 4 combinazioni. Pertanto, è necessario disporre di 4 singoli oggetti. Ciò diventerà complesso all'aumentare del tipo di crittografia.
Soluzione - Continuare a costruire lo stack - combinazioni a seconda delle necessità - in fase di esecuzione. Un altro vantaggio di questo approccio allo stack è che puoi rilassarlo facilmente.
Ecco la soluzione - in C ++.
Innanzitutto, hai bisogno di una classe base - un'unità fondamentale dello stack. Puoi pensare come base dello stack. In questo esempio, è un file chiaro. Seguiamo sempre il polimorfismo. Crea prima una classe di interfaccia di questa unità fondamentale. In questo modo, puoi implementarlo come desideri. Inoltre, non è necessario pensare alla dipendenza mentre si include questa unità fondamentale.
Ecco la classe di interfaccia -
class IclearData
{
public:
virtual std::string getData() = 0;
virtual ~IclearData() = 0;
};
IclearData::~IclearData()
{
std::cout<<"Destructor called of IclearData"<<std::endl;
}
Ora implementa questa classe di interfaccia -
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;
}
};
Ora, facciamo una classe astratta decoratore - che può essere estesa per creare qualsiasi tipo di sapore - qui il sapore è il tipo di crittografia. Questa classe astratta decoratore è correlata alla classe base. Pertanto, il decoratore "è un" tipo di classe di interfaccia. Pertanto, è necessario utilizzare l'ereditarietà.
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;
}
Ora creiamo una classe di decorazione in calcestruzzo - Tipo di crittografia - 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;
}
};
Ora, supponiamo che il tipo di decoratore sia 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;
}
};
Facciamo un codice client per usare questa classe decoratore -
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;
}
Vedrai i seguenti risultati:
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
Ecco il diagramma UML - Rappresentazione di classe di esso. Nel caso, desideri saltare il codice e concentrarti sull'aspetto del design.
strategy pattern
?
Il motivo Decoratore ti aiuta a cambiare o configurare una funzionalità del tuo oggetto concatenando altre sottoclassi simili di questo oggetto.
Il miglior esempio potrebbe essere rappresentato dalle classi InputStream e OutputStream nel pacchetto 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.
Che cos'è Decorator Design Pattern in Java.
La definizione formale del modello Decorator dal libro GoF (Design Patterns: Elements of Reusable Object-Oriented Software, 1995, Pearson Education, Inc. Publishing come Pearson Addison Wesley) afferma che puoi,
"Associare ulteriori responsabilità a un oggetto in modo dinamico. I decoratori offrono un'alternativa flessibile alla sottoclasse per estendere la funzionalità."
Diciamo che abbiamo una pizza e vogliamo decorarla con condimenti come pollo Masala, cipolla e mozzarella. Vediamo come implementarlo in Java ...
Programma per dimostrare come implementare Decorator Design Pattern in 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());
}
}
Ho usato ampiamente il modello Decorator nel mio lavoro. Ho pubblicato un post sul mio blog su come usarlo con la registrazione.
Il motivo decorativo consente di aggiungere dinamicamente il comportamento agli oggetti.
Facciamo un esempio in cui è necessario creare un'app che calcola il prezzo di diversi tipi di hamburger. È necessario gestire diverse varianti di hamburger, come "grande" o "con formaggio", ognuna delle quali ha un prezzo rispetto all'hamburger di base. Ad esempio aggiungere $ 10 per hamburger con formaggio, aggiungere $ 15 extra per hamburger di grandi dimensioni, ecc.
In questo caso potresti essere tentato di creare sottoclassi per gestirle. Potremmo esprimerlo in Ruby come:
class Burger
def price
50
end
end
class BurgerWithCheese < Burger
def price
super + 15
end
end
Nell'esempio sopra, la classe BurgerWithCheese eredita da Burger e sovrascrive il metodo del prezzo per aggiungere $ 15 al prezzo definito nella super classe. Dovresti anche creare una classe LargeBurger e definire il prezzo relativo a Burger. Ma devi anche definire una nuova classe per la combinazione di "grande" e "con formaggio".
Cosa succede se dobbiamo servire "hamburger con patatine fritte"? Abbiamo già 4 classi per gestire quelle combinazioni e dovremo aggiungerne altre 4 per gestire tutte le combinazioni delle 3 proprietà: "grande", "con formaggio" e "con patatine fritte". Abbiamo bisogno di 8 lezioni ora. Aggiungi un'altra proprietà e avremo bisogno di 16. Questo crescerà come 2 ^ n.
Invece, proviamo a definire un BurgerDecorator che accetta un oggetto 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
Nell'esempio sopra, abbiamo creato una classe BurgerDecorator, da cui eredita la classe BurgerWithCheese. Possiamo anche rappresentare la variazione "grande" creando la classe LargeBurger. Ora potremmo definire un grande hamburger con formaggio in fase di esecuzione come:
b = LargeBurger.new(cheese_burger)
b.price # => 50 + 15 + 20 = 85
Ricordi come l'utilizzo dell'ereditarietà per aggiungere la variante "with fries" comporterebbe l'aggiunta di altre 4 sottoclassi? Con i decoratori, creeremmo solo una nuova classe, BurgerWithFries, per gestire la nuova variante e gestirla in fase di esecuzione. Ogni nuova proprietà avrebbe bisogno di solo più decoratore per coprire tutte le permutazioni.
PS. Questa è la versione breve di un articolo che ho scritto sull'utilizzo del modello Decorator in Ruby , che puoi leggere se desideri scoprire esempi più dettagliati.
Decoratore:
Fare riferimento alla creazione di sorgenti all'articolo di per maggiori dettagli.
Decoratore (astratto) : è una classe / interfaccia astratta, che implementa l'interfaccia del componente. Contiene l'interfaccia Component. In assenza di questa classe, sono necessarie molte sottoclassi di ConcreteDecorator per diverse combinazioni. La composizione del componente riduce le sottoclassi non necessarie.
Esempio 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);;
}
Dai un'occhiata alla domanda SE di seguito per il diagramma UML e gli esempi di codice.
Articoli utili:
Vero esempio di parola del modello Decoratore: VendingMachineDecorator è stato spiegato @
Quando utilizzare il motivo decorativo?
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
Nell'esempio sopra, il tè o il caffè (bevanda) è stato decorato con zucchero e limone.
Il modello Decorator raggiunge un unico obiettivo di aggiungere dinamicamente responsabilità a qualsiasi oggetto .
Il modello I / O Java si basa sul modello del decoratore.
C'è un esempio su Wikipedia sulla decorazione di una finestra con barra di scorrimento:
http://en.wikipedia.org/wiki/Decorator_pattern
Ecco un altro esempio molto "reale" di "Membro del team, responsabile del team e manager", che illustra che il motivo decorativo è insostituibile con una semplice eredità:
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
Qualche tempo fa avevo refactored una base di codice nell'uso del modello Decorator, quindi cercherò di spiegare il caso d'uso.
Supponiamo che abbiamo un set di servizi e in base al fatto che l'utente abbia acquisito la licenza di un determinato servizio, dobbiamo avviare il servizio.
Tutti i servizi hanno un'interfaccia comune
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
}
}
Se osservi attentamente, ServiceSupport
dipende da LicenseManager
. Ma perché dovrebbe dipendere LicenseManager
? E se avessimo bisogno di un servizio in background che non ha bisogno di controllare le informazioni sulla licenza. Nella situazione attuale dovremo in qualche modo allenarci LicenseManager
per tornaretrue
per i servizi in background. Questo approccio non mi è sembrato buono. Secondo me il controllo delle licenze e altre logiche erano ortogonali tra loro.
Quindi motivo del decoratore viene in soccorso e qui inizia il refactoring con 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");
Facciamo un esempio di PubG. I fucili d'assalto funzionano meglio con lo zoom 4x e mentre ci siamo sopra, avremmo anche bisogno di compensatori e soppressori. Ridurrà il rinculo e ridurrà il suono di fuoco e l'eco. Dovremo implementare questa funzione in cui consentiremo ai giocatori di acquistare la loro pistola preferita e i loro accessori. I giocatori possono acquistare la pistola o alcuni degli accessori o tutti gli accessori e verrebbero addebitati di conseguenza.
Vediamo come viene applicato il motivo del decoratore qui:
Supponiamo che qualcuno desideri acquistare SCAR-L con tutti e tre gli accessori sopra menzionati.
Questo porterà a un diagramma di classe come questo:
Ora possiamo avere lezioni come questa:
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());
}
}
Allo stesso modo possiamo aggiungere anche altri accessori e decorare la nostra pistola.
Riferimento:
https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/
Motivo di disegno del decoratore : questo motivo aiuta a modificare le caratteristiche di un oggetto in fase di esecuzione. Fornisce sapori diversi a un oggetto e offre flessibilità per scegliere quali ingredienti vogliamo usare in quel sapore.
Esempio di vita reale: supponiamo che tu abbia un posto di cabina principale in un volo. Ora puoi scegliere più servizi con il sedile. Ogni comodità ha il suo costo associato. Ora, se un utente sceglie Wifi e cibo premium, verrà addebitato il costo per posto + wifi + cibo premium.
In questo caso il modello di design del decoratore può davvero aiutarci. Visita il link sopra per saperne di più sul modello del decoratore e sull'implementazione di un esempio di vita reale.