Wzorzec projektowy Dekorator

Problem

Mamy napisać oprogramowanie do sprzedaży napoi w kawiarni. Napoje mogą być różnego rodzaju oraz z najprzeróżniejszymi dodatkami, które można łączyć.

Rozwiązanie problemu

Naiwnym rozwiązaniem może być tworzenie klas dla każdej kombinacji napoju plus dodatków . Jednak takie rozwiązanie prowadzi do powstania ogromnej liczby klas. Wyobraźmy sobie, że sprzedajemy kawę oraz herbatę. Do tego dochodzą dodatki takie jak: cukier, mleko. Daje to nam klasy: Kawa, KawaPlusCukier, KawaPlusMleko, KawaPlusCukierPlusMleko, …  . Powstaje ogromna liczba klas. Gdy kawiarnia zmienia cenę cukru, to musimy zmienić jego cenę w kilku klasach. Kiedy dochodzi nowy dodatek, powstaje jeszcze więcej klas. Nie jesteśmy w stanie nad tym zapanować.

Z pomocą przychodzi wzorzec Dekorator. Mamy podstawowe klasy: Kawa, Herbata, Cukier oraz Mleko. Gdy klient zamawia herbatę z cukrem oraz mlekiem, to klasę Herbaty dekorujemy klasami Cukru oraz Mleka. Dzięki takiej metodzie, nie mnożą się nam klasy, a jakiekolwiek zmiany w jednej właściwości, nie przysparzają zmian w kilkunastu klasach.

Definicja wzorca

Pozwala na dynamiczne przydzielanie danemu obiektowi zachowań. Dekoratory dają elastyczność podobną do tej, jaką daje dziedziczenie, oferując jednak w zamian znaczenie rozszerzoną funkcjonalność.

Implementacja rozwiązania

Na początek abstrakcyjna klasa Napoju, która będzie bazą dla różnego rodzaju wariantów napoi:

public abstract class Napoj
{
    public virtual string Opis =>; "Napoj nieznany";
    public abstract decimal Cena { get; }
}

Przykładowe implementacje napoi:

public class Herbata : Napoj
{
    public override decimal Cena => 4.99m;
    public override string Opis => "Herbata";
}

public class Kawa : Napoj
{
    public override decimal Cena => 8.99m;
    public override string Opis => "Kawa";
}

Teraz przechodzimy do najważniejszej klasy naszego wzorca.

public abstract class NapojDekorator : Napoj
{
    protected readonly Napoj Napoj;
    public abstract override string Opis { get; }

    protected NapojDekorator(Napoj napoj)
    {
        Napoj = napoj;
    }
}

Dekorator dziedziczy z klasy Napoj oraz do konstruktora przekazujemy obiekt, który będziemy dekorować.

Przykładowe implementacje dodatków.

public class Cukier : NapojDekorator
{
public Cukier(Napoj napoj) : base(napoj)
{
}

public override string Opis => $"{Napoj.Opis}, Cukier";
public override decimal Cena => Napoj.Cena + 0.09m;
}

public class BitaSmietana : NapojDekorator
{
public BitaSmietana(Napoj napoj) : base(napoj)
{
}

public override string Opis => $"{Napoj.Opis}, Bita Smietana";
public override decimal Cena => Napoj.Cena + 0.99m;
}

public class Mleko : NapojDekorator
{
public Mleko(Napoj napoj) : base(napoj)
{
}

public override string Opis => $"{Napoj.Opis}, Mleko";
public override decimal Cena => Napoj.Cena + 0.49m;
}

Wszystkie dodatki dziedziczą z klasy dekoratora, rozszerzając działanie napoju.

Wywołanie kodu:

Napoj napoj1 = new Kawa();
napoj1 = new Mleko(new BitaSmietana(napoj1));

Napoj napoj2 = new Herbata();
napoj2 = new Mleko(new BitaSmietana(new Cukier(napoj2)));

Console.WriteLine($"Napoj1 : {napoj1.Opis}, cena: {napoj1.Cena}");
Console.WriteLine($"Napoj2 : {napoj2.Opis}, cena: {napoj2.Cena}");

Wynik działania:

dekorator_wynik_działania

Podsumowanie

  • Zapewnia większą elastyczność niż statyczne dziedziczenie.
  • Pozwala uniknąć tworzenia przeładowanych funkcjami klas na wysokich poziomach hierarchii.
  • Dekorator i powiązany z nim komponent nie są identyczne.
  • Każdy składnik może być udekorowany dowolną liczbą dekoratorów.
  • Zastosowanie dekoratoró powoduje powstanie dużej ilosci małych obiektów.

Źródła

  1. https://pl.wikipedia.org/wiki/Dekorator_(wzorzec_projektowy)
  2. http://cezarywalenciuk.pl/blog/programing/post/wzorce-projektowe-csharp–dekorator
Reklamy

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Connecting to %s