Ich habe das Dekorationsmuster studiert, wie es in GOF dokumentiert ist .
Bitte helfen Sie mir, das Dekorationsmuster zu verstehen . Könnte jemand ein Anwendungsbeispiel geben, wo dies in der realen Welt nützlich ist?
Ich habe das Dekorationsmuster studiert, wie es in GOF dokumentiert ist .
Bitte helfen Sie mir, das Dekorationsmuster zu verstehen . Könnte jemand ein Anwendungsbeispiel geben, wo dies in der realen Welt nützlich ist?
Antworten:
Das Dekorationsmuster erreicht ein einziges Ziel, jedem Objekt dynamisch Verantwortlichkeiten hinzuzufügen.
Stellen Sie sich einen Pizzaladen vor. In der Pizzeria werden sie nur wenige Pizzasorten verkaufen und sie werden auch Beläge auf der Speisekarte anbieten. Stellen Sie sich nun eine Situation vor, in der die Pizzeria Preise für jede Kombination aus Pizza und Belag angeben muss. Selbst wenn es vier einfache Pizzen und acht verschiedene Beläge gibt, würde die Anwendung verrückt werden, wenn all diese konkreten Kombinationen von Pizzen und Belägen beibehalten würden.
Hier kommt das Dekorationsmuster.
Gemäß dem Dekorationsmuster implementieren Sie Beläge, da Dekorateure und Pizzen von den Dekorateuren dieser Beläge dekoriert werden. Praktisch jeder Kunde möchte Beläge nach seinem Wunsch und der endgültige Rechnungsbetrag setzt sich aus den Basispizzas und zusätzlich bestellten Belägen zusammen. Jeder Dekorateur würde über die Pizzen, die er dekoriert, und den Preis Bescheid wissen. Die GetPrice () -Methode des Topping-Objekts gibt den kumulierten Preis sowohl für die Pizza als auch für das Topping zurück.
Hier ist ein Codebeispiel zur Erklärung oben.
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;
}
}
Dies ist ein einfaches Beispiel für das dynamische Hinzufügen eines neuen Verhaltens zu einem vorhandenen Objekt oder des Decorator-Musters. Aufgrund der Natur dynamischer Sprachen wie Javascript wird dieses Muster Teil der Sprache selbst.
// 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
oder eines einfachen if
Modells behaupten, dass dies ein großartiges Beispiel für das dynamische Hinzufügen von Verhalten zu einer Klasse ist. ABER wir benötigen mindestens zwei Klassen, um einen Dekorateur und dekorierte Objekte in diesem Muster zu definieren.
Es ist erwähnenswert, dass das Java-E / A-Modell auf dem Dekorationsmuster basiert. Die Überlagerung dieses Lesers mit diesem Leser über ... ist ein wirklich reales Beispiel für Dekorateur.
Beispiel - Szenario - Angenommen, Sie schreiben ein Verschlüsselungsmodul. Diese Verschlüsselung kann die gelöschte Datei mit DES - Data Encryption Standard verschlüsseln. Ebenso können Sie in einem System die Verschlüsselung als AES - Advance-Verschlüsselungsstandard verwenden. Sie können auch die Kombination von Verschlüsselung verwenden - zuerst DES, dann AES. Oder Sie können zuerst AES, dann DES haben.
Diskussion - Wie werden Sie mit dieser Situation umgehen? Sie können das Objekt solcher Kombinationen nicht weiter erstellen - zum Beispiel - AES und DES - insgesamt 4 Kombinationen. Sie benötigen also 4 einzelne Objekte. Dies wird komplex, wenn der Verschlüsselungstyp zunimmt.
Lösung - Bauen Sie die Stapelkombinationen je nach Bedarf zur Laufzeit weiter auf. Ein weiterer Vorteil dieses Stapelansatzes besteht darin, dass Sie ihn leicht abwickeln können.
Hier ist die Lösung - in C ++.
Erstens benötigen Sie eine Basisklasse - eine grundlegende Einheit des Stapels. Sie können als Basis des Stapels denken. In diesem Beispiel handelt es sich um eine klare Datei. Folgen wir immer dem Polymorphismus. Machen Sie zuerst eine Schnittstellenklasse dieser Grundeinheit. Auf diese Weise können Sie es nach Ihren Wünschen implementieren. Außerdem müssen Sie nicht an die Abhängigkeit denken, während Sie diese grundlegende Einheit einbeziehen.
Hier ist die Schnittstellenklasse -
class IclearData
{
public:
virtual std::string getData() = 0;
virtual ~IclearData() = 0;
};
IclearData::~IclearData()
{
std::cout<<"Destructor called of IclearData"<<std::endl;
}
Implementieren Sie nun diese Schnittstellenklasse -
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;
}
};
Lassen Sie uns nun eine abstrakte Klasse für Dekorateure erstellen, die erweitert werden kann, um jede Art von Geschmacksrichtungen zu erstellen. Hier ist die Geschmacksrichtung der Verschlüsselungstyp. Diese abstrakte Klasse des Dekorateurs ist mit der Basisklasse verwandt. Somit ist der Dekorator eine Art Schnittstellenklasse. Daher müssen Sie die Vererbung verwenden.
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;
}
Lassen Sie uns nun eine konkrete Dekorationsklasse erstellen - Verschlüsselungstyp - 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;
}
};
Angenommen, der Dekoratortyp ist 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;
}
};
Lassen Sie uns einen Client-Code erstellen, um diese Decorator-Klasse zu verwenden -
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;
}
Sie sehen die folgenden Ergebnisse -
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
Hier ist das UML-Diagramm - Klassendarstellung davon. Falls Sie den Code überspringen und sich auf den Designaspekt konzentrieren möchten.
strategy pattern
?
Mit dem Dekorationsmuster können Sie eine Funktionalität Ihres Objekts ändern oder konfigurieren, indem Sie sie mit anderen ähnlichen Unterklassen dieses Objekts verketten.
Das beste Beispiel wären die Klassen InputStream und OutputStream im Paket 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.
Was ist Decorator Design Pattern in Java?
Die formale Definition des Decorator-Musters aus dem GoF-Buch (Design Patterns: Elements of Reusable Object-Oriented Software, 1995, Pearson Education, Inc., Veröffentlichung als Pearson Addison Wesley) besagt, dass Sie Folgendes können:
"Fügen Sie einem Objekt dynamisch zusätzliche Verantwortlichkeiten hinzu. Dekorateure bieten eine flexible Alternative zur Unterklasse, um die Funktionalität zu erweitern."
Nehmen wir an, wir haben eine Pizza und möchten sie mit Belägen wie Hühnchen-Masala, Zwiebel und Mozzarella-Käse dekorieren. Mal sehen, wie man es in Java implementiert ...
Programm zur Demonstration der Implementierung von 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());
}
}
Ich habe bei meiner Arbeit ausgiebig Decorator-Muster verwendet. Ich habe in meinem Blog einen Beitrag darüber verfasst, wie man ihn mit der Protokollierung verwendet.
Mit dem Dekorationsmuster können Sie Objekten dynamisch Verhalten hinzufügen.
Nehmen wir ein Beispiel, in dem Sie eine App erstellen müssen, die den Preis für verschiedene Arten von Burgern berechnet. Sie müssen mit verschiedenen Burgervarianten umgehen, z. B. "groß" oder "mit Käse", von denen jeder einen Preis im Verhältnis zum Basisburger hat. ZB 10 USD für Burger mit Käse, 15 USD für großen Burger usw. hinzufügen
In diesem Fall könnten Sie versucht sein, Unterklassen zu erstellen, um diese zu behandeln. Wir könnten dies in Ruby ausdrücken als:
class Burger
def price
50
end
end
class BurgerWithCheese < Burger
def price
super + 15
end
end
Im obigen Beispiel erbt die BurgerWithCheese-Klasse von Burger und überschreibt die Preismethode, um dem in der Superklasse definierten Preis 15 USD hinzuzufügen. Sie würden auch eine LargeBurger-Klasse erstellen und den Preis relativ zu Burger definieren. Sie müssen aber auch eine neue Klasse für die Kombination von "groß" und "mit Käse" definieren.
Was passiert nun, wenn wir "Burger mit Pommes" servieren müssen? Wir haben bereits 4 Klassen, um diese Kombinationen zu handhaben, und wir müssen 4 weitere hinzufügen, um alle Kombinationen der 3 Eigenschaften zu handhaben - "groß", "mit Käse" und "mit Pommes". Wir brauchen jetzt 8 Klassen. Fügen Sie eine weitere Eigenschaft hinzu und wir benötigen 16. Dies wird als 2 ^ n wachsen.
Versuchen wir stattdessen, einen BurgerDecorator zu definieren, der ein Burger-Objekt aufnimmt:
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
Im obigen Beispiel haben wir eine BurgerDecorator-Klasse erstellt, von der die BurgerWithCheese-Klasse erbt. Wir können die "große" Variante auch darstellen, indem wir die LargeBurger-Klasse erstellen. Jetzt könnten wir einen großen Burger mit Käse zur Laufzeit definieren als:
b = LargeBurger.new(cheese_burger)
b.price # => 50 + 15 + 20 = 85
Erinnern Sie sich, wie die Verwendung der Vererbung zum Hinzufügen der Variante "mit Pommes" das Hinzufügen von 4 weiteren Unterklassen beinhalten würde? Mit Dekorateuren würden wir nur eine neue Klasse erstellen, BurgerWithFries, um die neue Variante zu handhaben und dies zur Laufzeit zu handhaben. Jede neue Eigenschaft würde nur mehr Dekorateur benötigen, um alle Permutationen abzudecken.
PS. Dies ist die Kurzversion eines Artikels, den ich über die Verwendung des Dekorationsmusters in Ruby geschrieben habe . Sie können ihn lesen, wenn Sie detailliertere Beispiele finden möchten.
Dekorateur:
Siehe Quellenherstellung Informationen finden Artikel .
Decorator (Abstract) : Dies ist eine abstrakte Klasse / Schnittstelle, die die Komponentenschnittstelle implementiert. Es enthält die Komponentenschnittstelle. In Abwesenheit dieser Klasse benötigen Sie viele Unterklassen von ConcreteDecorators für verschiedene Kombinationen. Die Zusammensetzung der Komponente reduziert unnötige Unterklassen.
JDK-Beispiel:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
char c = (char)bis.read();
System.out.println("Char: "+c);;
}
Schauen Sie sich die folgende SE-Frage für UML-Diagramme und Codebeispiele an.
Nützliche Artikel:
Echtes Wortbeispiel für ein Dekorationsmuster: VendingMachineDecorator wurde erklärt @
Wann sollte das Dekorationsmuster verwendet werden?
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
Im obigen Beispiel wurde Tee oder Kaffee (Getränk) von Zucker und Zitrone dekoriert.
Das Dekorationsmuster erreicht ein einziges Ziel , jedem Objekt dynamisch Verantwortlichkeiten hinzuzufügen .
Das Java-E / A-Modell basiert auf einem Dekorationsmuster.
Auf Wikipedia gibt es ein Beispiel für die Dekoration eines Fensters mit Bildlaufleiste:
http://en.wikipedia.org/wiki/Decorator_pattern
Hier ist ein weiteres sehr "reales" Beispiel für "Teammitglied, Teamleiter und Manager", das zeigt, dass das Dekorationsmuster durch einfache Vererbung unersetzbar ist:
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
Vor einiger Zeit hatte ich eine Codebasis in das Decorator-Muster umgestaltet, daher werde ich versuchen, den Anwendungsfall zu erklären.
Nehmen wir an, wir haben eine Reihe von Diensten und basierend darauf, ob der Benutzer die Lizenz für einen bestimmten Dienst erworben hat, müssen wir den Dienst starten.
Alle Dienste haben eine gemeinsame Schnittstelle
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
}
}
Wenn Sie genau beobachten, ServiceSupport
ist abhängig von LicenseManager
. Aber warum sollte es abhängig sein LicenseManager
? Was wäre, wenn wir einen Hintergrunddienst benötigen, der die Lizenzinformationen nicht überprüfen muss? In der gegenwärtigen Situation müssen wir irgendwie trainieren, LicenseManager
um zurückzukehrentrue
für Hintergrunddienste zurückzukehren. Dieser Ansatz schien mir nicht gut zu sein. Meiner Meinung nach waren Lizenzprüfung und andere Logik orthogonal zueinander.
Also kommt Decorator Pattern zur Rettung und hier beginnt das Refactoring mit 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");
Nehmen wir ein Beispiel für PubG. Sturmgewehre funktionieren am besten mit 4-fachem Zoom und während wir darauf sind, würden wir auch Kompensator und Suppressor benötigen. Es reduziert den Rückstoß und reduziert das Schussgeräusch sowie das Echo. Wir müssen diese Funktion implementieren, damit die Spieler ihre Lieblingswaffe und ihr Zubehör kaufen können. Spieler können die Waffe oder einen Teil des Zubehörs oder das gesamte Zubehör kaufen und werden entsprechend belastet.
Mal sehen, wie das Dekorationsmuster hier angewendet wird:
Angenommen, jemand möchte SCAR-L mit allen drei oben genannten Zubehörteilen kaufen.
Dies führt zu einem Klassendiagramm wie dem folgenden:
Jetzt können wir Klassen wie diese haben:
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());
}
}
In ähnlicher Weise können wir auch anderes Zubehör hinzufügen und unsere Waffe dekorieren.
Referenz:
https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/
Decorator Design Pattern : Dieses Muster hilft, die Eigenschaften eines Objekts zur Laufzeit zu ändern. Es bietet einem Objekt verschiedene Geschmacksrichtungen und gibt Flexibilität bei der Auswahl der Zutaten, die wir für diese Geschmacksrichtung verwenden möchten.
Beispiel aus dem wirklichen Leben: Nehmen wir an, Sie haben einen Hauptkabinensitz in einem Flug. Jetzt können Sie mit dem Sitz mehrere Annehmlichkeiten auswählen. Jede Annehmlichkeit hat ihre eigenen Kosten. Wenn sich ein Benutzer für Wifi und Premium-Essen entscheidet, wird ihm Sitzplatz + Wifi + Premium-Essen in Rechnung gestellt.
In diesem Fall kann uns das Dekorationsmuster wirklich helfen. Besuchen Sie den obigen Link, um mehr über das Dekorationsmuster und die Implementierung eines realen Beispiels zu erfahren.