poniedziałek, 12 października 2009

Decorator Pattern

Wzorzec ten pozwala na dodawanie nowej funkcjonalności dynamicznie (podczas działania programu). Jest też elastyczną alternatywą dla dziedziczenia. Wzorzec ten wykorzystuje mechanizm wraperów (opakowań).

ConcreteComponent - klasa którą zamierzamy "udekorować"
 Decorator - klasa abstrakcyjna po której dziedziczą konkretne dekoratory, powiększające "koszyk" możliwości klasy ConcreteComponent


Aby lepiej sobie całą sytuację wyobrazić rozważmy sobie przykład. Mamy klasę Kawa. po klasie Kawa może dziedziczyć np kawa zbożowa, espresso, java itd. Do każdej kawy można dodać cukier, mleko czy też mokkę (gatunek kawy arabskiej). Tak więc w naszym założeniu moglibyśmy do naszych klas np. dodać 3 właściwości bool o nazwach: mleko, cukier, mokka. Jednak teraz wyobraźmy sobie, że doszedł nam nowy rodzaj kawy oraz nowy dodatek (śmietanka). Musimy przejść przez wszystkie nasze klasy i dodać (rozszerzyć) odpowiednie funkcje dzięki którym będziemy wiedzieli z czym pijemy naszą kawę.

Aby uniknąć takiej sytuacji warto użyć w tym miejscu wzorca projektowego Decorator. Pozwala on na bardzo łatwe zwiększanie funkcjonalności podstawowych klas. Wracając do naszego przykładu z kawą:

I kod:

    8     public abstract class Coffe
    9     {
   10         protected string description = "Unknow Coffe";
   11
   12         public virtual string GetDescription()
   13         {
   14             return description;
   15         }
   16
   17         public virtual double Cost()
   18         {
   19             return 0;
   20         }
   21     }

    8     public class Jawajska : Coffe
    9     {
   10         public override double Cost()
   11         {
   12             return 2.0;
   13         }
   14         public override string GetDescription()
   15         {
   16             return "Jawajska";
   17         }
   18     }

    8     public abstract class CondimentsDecorator : Coffe
    9     {
   10         protected Coffe coffe;
   11     }

    8     public class Milk : CondimentsDecorator
    9     {
   10         
   11
   12         public Milk(Coffe coffe)
   13         {
   14             this.coffe = coffe;
   15         }
   16
   17         public override double Cost()
   18         {
   19             return 0.2 + coffe.Cost();
   20         }
   21
   22         public override string GetDescription()
   23         {
   24             return coffe.GetDescription() + ", Milk";
   25         }
   26     }

    8     public class Mokka : CondimentsDecorator
    9     {
   10      
   11         public Mokka(Coffe coffe)
   12         {
   13             this.coffe = coffe;
   14         }
   15
   16         public override double Cost()
   17         {
   18             return 0.25 + coffe.Cost();
   19         }
   20
   21         public override string GetDescription()
   22         {
   23             return coffe.GetDescription() + ", Mokka";
   24         }
   25     }

    8     public class Suggar : CondimentsDecorator
    9     {
   10         
   11
   12         public Suggar(Coffe coffe)
   13         {
   14             this.coffe = coffe;
   15         }
   16
   17         public override double Cost()
   18         {
   19             return 0.15 + coffe.Cost();
   20         }
   21
   22         public override string GetDescription()
   23         {
   24             return coffe.GetDescription() + ", suggar";
   25         }
   26     }



    8     class Program
    9     {
   10         static void Main(string[] args)
   11         {
   12             Coffe coffe = new Jawajska();
   13             Console.WriteLine(coffe.GetDescription());
   14             Console.WriteLine(coffe.Cost());
   15             coffe = new Milk(coffe);
   16             coffe = new Suggar(coffe);
   17             coffe = new Mokka(coffe);
   18             Console.WriteLine(coffe.GetDescription());
   19             Console.WriteLine(coffe.Cost());
   20         }
   21     }


Wynikiem działania powyższego kodu będzie:

Na koniec jeszcze małe podsumowanie na temat użycia tego wzorca.
Przydaje się on wszędzie gdzie mamy potrzebę "przyozdabiania". Przydać się może np. w aplikacji która pracuje na plikach graficznych, edycji wideo itp.

Wzorzec ten jest szczególnie wykorzystywany w przypadku biblioteki I/O.

Brak komentarzy:

Prześlij komentarz