Dekorateur-Design-Muster

Dec 27 2022
Ich kann mich selbst an die erste Stelle setzen, auch wenn ich möchte. Wir haben eine Schnittstelle namens Zahlung, die eine Zahlungsmethode enthält. Und eine konkrete Klasse DefaultPayment zum Implementieren der Methode, die Methode nimmt den Preis als Argument für die Zahlungslogik.

Ich kann mich selbst an die erste Stelle setzen, auch danach, wenn ich will

Sie können vorher Zucker oder sogar Schokolade nach der Zubereitung Ihrer Tasse Nescafe hinzufügen

Wir haben eine Schnittstelle namens Payment , die eine Zahlungsmethode enthält .

Und eine konkrete Klasse DefaultPayment zum Implementieren der Methode, die Methode nimmt den Preis als Argument für die Zahlungslogik.

public interface Payment {
    void pay(BigDecimal price);
}
public class DefaultPayment implements Payment {
    @Override
    public void pay(BigDecimal price) {
        System.out.println("Payed: " + price);
    }
}

public class Client {
    public static void main(String[] args) {
        Payment defaultPayment = new DefaultPayment();
        defaultPayment.pay(BigDecimal.valueOf(100));
    }
}

Angenommen, es gibt eine Steuer auf ein Produkt, für das ich bezahlen möchte, was kann ich tun?

Wir können unser Design nutzen, wir haben es für eine Schnittstelle entwickelt, nicht für eine Implementierung.

Wir können also eine Klasse für Zahlung erstellen und sie zu einer Art Zahlung machen und gleichzeitig die Zahlung als Eigenschaft halten, und sie könnte die Zahlung mit ihrem Konstruktor entgegennehmen, und wir nennen diese Klasse TaxPaymentDecorator .

public class TaxPaymentDecorator implements Payment {
    private Payment payment;
    private BigDecimal tax = BigDecimal.ZERO;

    public TaxPaymentDecorator(Payment payment) {
        this.payment = payment;
    }

    @Override
    public void pay(BigDecimal price) {
        price = price.add(this.tax);
        payment.pay(price);
    }

    public BigDecimal getTax() {
        return tax;
    }

    public void setTax(BigDecimal tax) {
        this.tax = tax;
    }
}

Und der Client könnte den TaxPaymentDecorator wie folgt verwenden:

public class Client {
    public static void main(String[] args) {
        TaxPaymentDecorator taxPaymentDecorator = new TaxPaymentDecorator(new DefaultPayment());
        taxPaymentDecorator.setTax(BigDecimal.valueOf(50));
        taxPaymentDecorator.pay(BigDecimal.valueOf(100));
    }
}

public class DiscountPaymentDecorator implements Payment {
    private Payment payment;
    private BigDecimal discount = BigDecimal.ZERO;

    public DiscountPaymentDecorator(Payment payment) {
        this.payment = payment;
    }

    @Override
    public void pay(BigDecimal price) {
        price = price.subtract(discount);
        payment.pay(price);
    }

    public BigDecimal getDiscount() {
        return discount;
    }

    public void setDiscount(BigDecimal discount) {
        this.discount = discount;
    }
}

public class Client {
    public static void main(String[] args) {
        TaxPaymentDecorator taxPaymentDecorator = new TaxPaymentDecorator(new DefaultPayment());
        taxPaymentDecorator.setTax(BigDecimal.valueOf(50));
        DiscountPaymentDecorator discountPaymentDecorator = new DiscountPaymentDecorator(taxPaymentDecorator);
        discountPaymentDecorator.setDiscount(BigDecimal.valueOf(10));
        discountPaymentDecorator.pay(BigDecimal.valueOf(100));
    }
}

UML-Diagramm

Decorator-Entwurfsmuster UML-Diagramm

Design-Prinzipien

Ich werde hier drei Designprinzipien diskutieren:

  1. Design für eine Schnittstelle, nicht für die Implementierung: Wie Sie sehen, hat uns die Verwendung der Zahlungsschnittstelle geholfen, die Leistungsfähigkeit von OOP zu nutzen, indem Verhalten dynamisch zur Laufzeit hinzugefügt wurde, ohne dass Änderungen für die alte Implementierung vorgenommen wurden.
  2. Machen Sie Ihr Design offen für Erweiterungen und schließen Sie es für Änderungen: Dieser Punkt hängt mit dem vorherigen zusammen, unsere alte Implementierung hat uns nicht gezwungen, Änderungen vorzunehmen, aber wir haben die alte Implementierung erweitert, um neue Verhaltensweisen hinzuzufügen, und ich meine hier stattdessen die Verwendung der Schnittstelle des Designs für die Umsetzung.
  3. Bevorzugen Sie die Komposition gegenüber der Vererbung: Der Dekorateur verwendet hier die Komposition, anstatt zu versuchen, die Vererbung zu verwenden. Im Allgemeinen kann die Verwendung der Vererbung Probleme verursachen, indem die Kopplung erhöht wird, und wir könnten dies in einem separaten Blog diskutieren.

Weisen Sie einem Objekt dynamisch zusätzliche Verantwortlichkeiten zu. Dekorateure bieten eine flexible Alternative zur Unterklassifizierung, um die Funktionalität zu erweitern.

In unserem Beispiel haben wir zur Laufzeit dynamisch zwei Verantwortlichkeiten hinzugefügt, wir addieren die Steuer zum Preis und subtrahieren den Rabatt vom Preis, wir haben keine Unterklassifizierung für die DefaultPayment vorgenommen, wir haben die Leistungsfähigkeit von OOP genutzt , um unsere Verhaltensweisen auf flexible Weise hinzuzufügen, und das ist das Decorator-Muster .