Kiedy stosować wzór dekoratora?

Przeglądam moje wzorce projektowe, a jeden wzór, który muszę jeszcze poważnie wykorzystać w moim kodowaniu, to wzór dekoratora.

Rozumiem wzór, ale to, co chciałbym wiedzieć, to kilka dobrych konkretnych przykładów czasów w prawdziwym świecie, że wzór dekoratora jest najlepszym / optymalnym / eleganckim rozwiązaniem. Konkretne sytuacje, w których potrzeba wzoru dekoratora jest naprawdę przydatna.

Dzięki.
Author: Ravindra babu, 2009-10-11

7 answers

Wzór dekoratora jest często używany ze strumieniami: możesz owinąć strumień strumieniem, aby uzyskać dodatkową funkcjonalność. Widziałem to z. Net framework - z tego co wiem to występuje gdzie indziej. Moim ulubionym jest używanie GZipStream wokół strumienia plików, dla dodatkowej kompresji.

 35
Author: Charlie Salts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-11 03:14:18

Wzór dekoratora służy do dodawania dodatkowych funkcji do określonego obiektu w przeciwieństwie do klasy obiektów. Łatwo jest dodać funkcjonalność do całej klasy obiektów poprzez podklasowanie obiektu, ale nie jest możliwe rozszerzenie pojedynczego obiektu w ten sposób. Za pomocą wzoru dekoratora można dodać funkcjonalność do pojedynczego obiektu i pozostawić inne podobne do niego niezmodyfikowane.

W Javie klasycznym przykładem wzorca dekoratora jest Java i / o Streams wdrożenie.

FileReader       frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);

Poprzedni kod tworzy czytnik -- lrdr -- który odczytuje z pliku i śledzi numery linii. Linia 1 tworzy czytnik plików (frdr), a linia 2 dodaje śledzenie numeru linii.

Zachęcam do zapoznania się z kodem źródłowym Javy dla klas I/O Javy.
 65
Author: Pascal Thivent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-11 03:32:22

Ostatnio użyłem wzorca dekoratora w usłudze internetowej, która używa następującego interfejsu CommandProcessor:

public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);

Zasadniczo, Commandprocesor otrzymuje żądanie i tworzy odpowiednie polecenie, wykonuje polecenie i tworzy odpowiednią odpowiedź i wysyła odpowiedź. Kiedy chciałem dodać timing i zalogować go, stworzyłem TimerDecorator, który używał istniejącego Commandprocessora jako komponentu. TimerDecorator implementuje interfejs CommandProcessor, ale tylko dodaje timing, a następnie wywołuje swój cel, który jest prawdziwym Commandprocessorem. Coś takiego:

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
   }

}

Więc prawdziwy procesor komend jest zawinięty wewnątrz Timerdecoratora i mogę traktować TimerDecorator tak jak docelowy procesor komend, ale teraz została dodana logika czasowa.

 22
Author: Kaleb Brasee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-11 03:28:56

Dekorator jest prosty, ale niezwykle potężny. Ma kluczowe znaczenie dla osiągnięcia rozdzielenia obaw i jest niezbędnym narzędziem dla zasady otwartej zamkniętej. Weźmy przykład składania zamówienia na produkt:

IOrderGateway
{
    void PlaceOrder(Order order);
{

Główna realizacja: AmazonAffiliateOrderGateway

Możliwymi dekoratorami mogą być:

  • IncrementPerformanceCounterOrderGateway
  • QueueOrderForLaterOnTimeoutOrderGateway
  • EmailOnExceptionOrderGateway
  • InterceptTestOrderAndLogOrderGateway

Spójrz tutaj dla bardziej szczegółowego przykładu.

 7
Author: Mike Valenty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-23 09:28:20

Dekorator wzorzec dynamicznie zmienia funkcjonalność obiektu w czasie działania bez wpływu na istniejącą funkcjonalność obiektów.

Kluczowe przypadki użycia:

  1. dodawanie dodatkowych funkcjonalności/obowiązków dynamicznie
  2. Usuń funkcjonalności / obowiązki dynamicznie
  3. unikaj zbyt dużego podklasowania , aby dodać dodatkowe obowiązki.

Wady:

  1. nadużywanie Zasady Open Closed (otwarte dla rozszerzenia i zamknięte dla modyfikacji). Używaj tej funkcji oszczędnie tam, gdzie kod jest najmniej prawdopodobny.
  2. zbyt wiele małych klas i doda koszty utrzymania .

Przykład z prawdziwego świata: Oblicz cenę napoju, który może zawierać wiele smaków.

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();
    }
}

Wyjście:

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

Ten przykład oblicza koszt napoju w automacie po dodaniu wielu smaków do napoju.

W powyższym przykładzie:

Koszt herbaty = 10, cytryny = 3 i cukru = 5. Jeśli zrobisz cukier + cytryna + herbata, kosztuje 18. Koszt kawy =15, cytryny = 3, cukru = 5. Jeśli robisz cukier + cytryna + Kawa, kosztuje 23

Dzięki zastosowaniu tego samego dekoratora dla obu napojów (herbaty i kawy) zmniejszono liczbę podklas. W przypadku braku wzoru dekoratora należy różne podklasy dla różnych kombinacji.

Kombinacje będą takie:

SugarLemonTea
SugarTea
LemonTea

SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino

Itd.

Stosując ten sam dekorator dla obu napojów, zmniejszono liczbę podklas. Jest to możliwe ze względu na skład, a niedziedziczenie koncepcja użyta w tym wzorze.

Powiązane pytanie:

Wzór dekoratora dla IO

przydatne linki:

Design-wzory-dekorator by dzone

Dekorator by sourcemaking

Oodesign Artykuł

 7
Author: Ravindra babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 10:18:18

Zend Framework używa dekoratora dla elementów formularzy

Trochę więcej informacji: http://framework.zend.com/manual/en/zend.form.decorators.html

 3
Author: Mike B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-11 03:13:43
  1. aby dodawać zadania do poszczególnych obiektów dynamicznie i przejrzyście.
  2. za obowiązki, które można wycofać.
  3. gdy rozszerzenie przez podklasowanie jest niepraktyczne. Czasami możliwa jest duża liczba niezależnych rozszerzeń i spowodowałaby eksplozję podklas obsługujących każdą kombinację.
 2
Author: Karim Haddad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-29 00:30:52