Feb 8 2009

Wzorce projektowe: State

Wzorzec State jest wzorcem operacyjnym, który posiada wiele implementacji i może się przełączać pomiędzy nimi podczas działania programu. Zmiana może być spowodowana interakcją użytkownika lub też automatycznie przez klasę kliencką jako odpowiedź na wykonaną akcję. Korzystanie z wzorca eliminuje wiele instrukcji warunkowych z kodu, które uzależnieją wykonane operacji od odpowiedniego warunku. W state takie warunki przeniesione są do oddzielnych klas.

Spójrzmy na kod:

 1 public class BadStateKlas {
 2     private boolean warunek = true;
 3
 4     public static void main(String[] args) {
 5         BadStateKlas bad = new BadStateKlas();
 6     }
 7
 8     public BadStateKlas() {
 9
10         metoda1();
11         metoda2();
12
13     }
14
15     private void metoda1() {
16         //kod wykonujemy gdy wartość pola 'warunek' jest = true
17         if (warunek) {
18             System.out.println("Wykonuję metodę 1");
19         } else {
20             System.out.println("Nie wykonuję metody 1");
21         }
22     }
23
24     private void metoda2() {
25         // kod wykonujemy gdy wartość pola 'warunek' jest = false
26         if (!warunek) {
27             System.out.println("Wykonuję metodę 2");
28         } else {
29             System.out.println("Nie wykonuję metody 2");
30         }
31     }
32 }
33 

Kod wykonywany w metodach 1 i 2 jest zależny od wartości pola ‘warunek’. A co jeżeli będę trzy możliwości? Pole warunek trzeba będzie zmienić z boolean na int lub Enum, a listy ‘ifów’ będą coraz dłuższe. Zamiast tego ustawimy pole stan dla klasy, a kod zależny od tego pola umieścimy w zewnętrznych klasach.
Interfejs:

 1 public interface Warunek {
 2     public void metoda1();
 3
 4     public void metoda2();
 5 }
 6 

Poszczególne stany:

 1 public class Warunek1 implements Warunek{
 2     public void metoda1() {
 3         System.out.println("Wykonuję metodę 1");
 4     }
 5
 6     public void metoda2() {
 7         System.out.println("Nie wykonuję metody 2");
 8     }
 9 }
10 
 1 public class Warunek2 implements Warunek{
 2     public void metoda1() {
 3         System.out.println("Nie wykonuję metody 1");
 4     }
 5
 6     public void metoda2() {
 7         System.out.println("Wykonuję metodę 2");
 8     }
 9 }
10 

klasa klienta:

 1 public class GoodState {
 2     private Warunek warunek;
 3
 4     public GoodState(Warunek warunek) {
 5         this.warunek = warunek;
 6
 7         warunek.metoda1();
 8         warunek.metoda2();
 9
10     }
11
12     public void setWarunek(Warunek warunek) {
13         this.warunek = warunek;
14     }
15
16     public static void main(String[] args) {
17         GoodState state = new GoodState(new Warunek1());
18     }
19 }
20 

Diagram wzorca:

  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 8 2009

Wzorce projektowe: Proxy

Proxy jest strukturalnym wzorcem projektowym wykorzystywanym do reprezentowania skomplikowanego obiektu lub obiektu, którego utworzenie wymaga dużego nakładu czasu, za pomocą obiektu prostego. Jeżeli tworzenie obiektu jest czasochłonne lub zasobożerne, Proxy pozwala odłożyć akt tworzenia tego obiektu na czas, w którym ten obiekt rzeczywiście będzie potrzebny. Proxy ma taki sam interfejs jak obiekt którego reprezentuje ( a tak właściwie to nie muszą implementować tego samego interfejsy – wymagane jest tylko aby proxy posiadało definicje wszystkich metod obiektu docelowego). Kiedy pełny obiekt zostanie stworzony, Proxy przekazuje do niego wszystkie wywołania metod.
Proxy stosuje się:
- kiedy obiekt potrzebuje dużo czasu na załadowanie,
- kiedy obiekt znajduje się na odległej maszynie i ładowanie go przez sieć może być długotrwałe,
- kiedy chcemy aby prawa dostępu do obiektu były ograniczone – proxy sprawdza uprawnienia użytkownika, przed załadowaniem odpowiedniego obiektu.
Bardzo często wzorzec proxy wykorzystuje się wraz z mechanizmami RMI.
Rodzaje proxy:
Copy-On-Write Proxy – odracza kopiowanie obiektu docelowego do momentu kiedy jest potrzebny klientowi,
Protection Proxy – różnym klientom dostarcza różny poziom dostępu do obiektu docelowego,
Cache Proxy – w tymczasowej pamięci przechowuje wyniki kosztownych operacji obiektu docelowego, po to by klienci mogli je dzielić,
Firewall Proxy – chroni obiekt docelowy przed niepowołowanymi klientami,
Smart Reference Proxy – dostarcza dodatkowe operacje w momencie odwołania do obiektu docelowego, jak np. liczba odwołań do obiektu docelowego.

 1 public interface ProxyBase {
 2     public void metoda();
 3 }
 4 
 1 public class Implementacja implements ProxyBase{
 2     public void metoda() {
 3         System.out.println("Wykonuję metodę: metoda");
 4     }
 5 }
 6 
 1 public class Proxy implements ProxyBase {
 2     private Implementacja implementacja;
 3
 4     public Proxy() {
 5         this.implementacja = new Implementacja();
 6     }
 7
 8     public void metoda() {
 9         implementacja.metoda();
10     }
11 }
12 
  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 8 2009

Wzorce projektowe: Bridge

Wzorzec projektowy Bridge jest strukturalnym wzorcem projektowym, który pozwala oddzielić abstrakcję obiektu od jego implementacji przez co mogą być oddzielnie rozszerzane. Pozwala to wprowadzać oddzielnie zmiany do każdej z dwóch hierarchii klas.
Działanie wzorca polega na tym, że abstrakcja definiuje ogólny wygląd obiektu, a jego działania są przekazywane do implementacji.
Implementacja niejednokrotnie jest uzależniona od systemu zewnętrznego i może być dostarczona przez któryś z wzorców produkujących. Dla przykładu może istnieć abstrakcja Rysunek i mieć metodę rysuj(), która będzie przeniesiona do implementacji ponieważ inaczej się rysuje na papierze a inaczej na ekranie.

Abstrakcja:

 1 public abstract class Rysunek {
 2     protected RysunekImpl rysunekImpl;
 3
 4     protected int x;
 5     protected int y;
 6
 7     public Rysunek( int x, int y, RysunekImpl rysunekImpl) {
 8         this.x = x;
 9         this.y = y;
10         this.rysunekImpl = rysunekImpl;
11     }
12
13     public void powiększ(int i) {
14         this.x *= i;
15         this.y *= i;
16     }
17     public abstract void rysuj();
18 }

Konkretny obiekt:

 1 public class KonkretnyRysunek extends Rysunek{
 2 
 3     public KonkretnyRysunek(int x, int y, RysunekImpl rysunekImpl) {
 4         super(x, y, rysunekImpl);
 5     }
 6
 7     public void rysuj() {
 8         rysunekImpl.rysuj(x, y);
 9     }
10 }
11 

Implementacja i konkretni implementatorzy:

 1 public interface RysunekImpl {
 2     public void rysuj(int x , int y);
 3 }
 4 
 1 public class Drukowanie implements RysunekImpl{
 2     public void rysuj(int x, int y) {
 3         System.out.printf("Drukuję obrazek o wymiarach %d na %d \n", x, y);
 4     }
 5 }
 6 
 1 public class RysowanieNaEkranie implements RysunekImpl{
 2     public void rysuj(int x, int y) {
 3         System.out.printf("Rysuję na ekranie rysunek o wymiarach %d na %d \n",x,y );
 4     }
 5 }
 6 

Klient:

 1 public class BridgeTester {
 2     public static void main(String[] args) {
 3         List<Rysunek> rysunki = new ArrayList<Rysunek>();
 4
 5         rysunki.add(new KonkretnyRysunek(2, 3, new Drukowanie()));
 6
 7         rysunki.add(new KonkretnyRysunek(3, 4, new RysowanieNaEkranie()));
 8
 9         for (Rysunek r : rysunki) {
10             r.powiększ(2); //abstrakcja
11             r.rysuj(); //implementacja
12         }
13     }
14 }
15 
  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 8 2009

Wzorce projektowe: Composite

Wzorzec projektowy Composite jest strukturalnym wzorcem projektowym wykorzystywanym przy tworzeniu hierarchiczne struktury (drzewiastej) złożonej z obietków. Pozwala traktować jednakowo obiekty jak i grupy obiektów.
Aplikacji dostarczają możliwość grupowania obiektów, jednak wykonanie operacji na pojedyńczych obiektach i ich kontenerach są różne.
Kompozyt definiuje interfejs, który jest wspólny zarówno dla pojedyńczych jednostek jak i ich grup. W interfejsie tym są zdefiniowane wszystkie metody charakterysytyczne dla obu tych obiektów przez co są traktowane tak samo przez klienta. Jednak implementacja tych metod jest różna w pojedyńczych obiektach i inna w grupach.
Wzorzec kompozyt wykorzystuje się najczęściej w sytuacji gdy chcemy reprezentować całą hierarchię obietków 9lub tylko część hierarchii), lub gdy chcemy aby klient nie widział różnicy między pojedyńczym obiektem a ich kolekcją. Klient będzie traktował wszystkie obiekty jednakowo.

 1 public interface JednostkaOrganizacyjna {
 2
 3     public void dodajJednostke(JednostkaOrganizacyjna jo);
 4
 5     public void usuńJednostke(JednostkaOrganizacyjna jo);
 6
 7     public int wyliczPensję();
 8
 9     public String getName();
10 }
11 
 1 public class Biuro implements JednostkaOrganizacyjna {
 2     private List<JednostkaOrganizacyjna> pracownicy = 
             new ArrayList<JednostkaOrganizacyjna>();
 3     private String name;
 4
 5     public void dodajJednostke(JednostkaOrganizacyjna jo) {
 6         pracownicy.add(jo);
 7
 8     }
 9
10     public Biuro(String name) {
11         this.name = name;
12     }
13
14     public void usuńJednostke(JednostkaOrganizacyjna jo) {
15         pracownicy.remove(jo);
16     }
17
18     public int wyliczPensję() {
19         int razem = 0;
20         System.out.println(name + ":");
21         for (JednostkaOrganizacyjna pracownik : pracownicy) {
22             razem += pracownik.wyliczPensję();
23         }
24
25         System.out.println("Razem pensja w biurze: " + name + " " + razem);
26         return razem;
27
28     }
29
30     public String getName() {
31         return name;
32     }
33 }
34 
 1 public class Pracownik implements JednostkaOrganizacyjna{
 2     private String name;
 3     private int pensja;
 4
 5     public Pracownik(String name, int pensja) {
 6         this.name = name;
 7         this.pensja = pensja;
 8     }
 9
10     public void dodajJednostke(JednostkaOrganizacyjna jo) {
11         System.out.println("Nie mogę dodać nic do osoby");
12     }
13
14     public void usuńJednostke(JednostkaOrganizacyjna jo) {
15         System.out.println("Skoro nie mogę dodać to i nie mogę odjąć");
16     }
17
18     public int wyliczPensję() {
19         System.out.println(name+ " "+ pensja);
20         return pensja;
21     }
22
23     public String getName() {
24         return name;
25     }
26 }
27 
 1 public class Klient {
 2     public static void main(String[] args) {
 3         JednostkaOrganizacyjna spółka = new Biuro("Spółka ABC");
 4
 5         JednostkaOrganizacyjna szef = new Pracownik("Jak Kowalski", 15000);
 6
 7         JednostkaOrganizacyjna księgowość = new Biuro("Księgowość");
 8
 9         JednostkaOrganizacyjna ewa = new Pracownik("Ewa Nowak", 6000);
10
11         JednostkaOrganizacyjna adam = new Pracownik("Adam Nowak", 6000);
12
13         spółka.dodajJednostke(szef);
14         spółka.dodajJednostke(księgowość);
15
16         księgowość.dodajJednostke(ewa);
17         księgowość.dodajJednostke(adam);
18
19         spółka.wyliczPensję();
20
21     }
22 }
23 

Diagram wzorca:

  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 7 2009

Wzorce projektowe: Template method

Wzorzec projektowy Template method jest wzorcem z grupy wzorców operacyjnych. Definiuje on za pomocą abstrakcji szkielet algorytmu, który w klasach potomnych jest w pełni realizowany. Na początku tworzona jest kalsa zawierająca ogólne kroki algorytmu zapisane jako metody abstrakcyjne. Klasy potomne nadpisują te abstrakcyjne metody implementując rzeczywiste akcje. Dzięki takiemu podejściu szkielet algorytmu trzymany jest w jednym miejscu, ale jego mniejsze (bardziej specjalistyczne )kroki moga być zmieniane w podklasach.
Template Method jest wykorzystywany we wzorcach Strategia i Abstract Factory.

Dla przykładu:

 1 public abstract class Generalizacja {
 2     public void wykonajAlogrytm() {
 3         krok1();
 4         krok2();
 5         krok3();
 6         krok4();
 7     }
 8
 9     protected void krok1() {
10         System.out.println("Wykonuję krok 1...");
11     }
12
13     protected abstract void krok2();
14
15     protected abstract void krok3();
16
17     protected void krok4() {
18         System.out.println("Wykonuję krok 4...");
19     }
20 }
21 
 1 public class Specjalizacja extends Generalizacja{
 2     protected void krok2() {
 3         System.out.println("Krok 2 specjalizacji...");
 4     }
 5
 6     protected void krok3() {
 7         System.out.println("Krok 3 specjalizacji...");
 8     }
 9 }
10 
 1 public class Realizacja {
 2     public static void main(String[] args) {
 3         Generalizacja generalizacja = new Specjalizacja();
 4
 5         generalizacja.wykonajAlogrytm();
 6     }
 7 }
 8 

Diagram wzorca:

  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 7 2009

Wzorce projektowe: Facade

Wzorzec projektowy Facade należy do grupy wzorców strukturalnych, którego rolą jest uproszczenie złożonego interfejsu lub jego uporządkowanie. Czasami występuje potrzeba skorzystania z gotowego, niejednokrotnie rozbudowanego systemu albo biblioteki. jednocześnie nie ma potrzeb na korzystanie z całego dostępnego API lub to API jest nieprzejrzyste. Buduje się wtedy obiekty posredniczące między starym systemem, a systemem, który z niego korzysta.
Kiedy wykorzystuje się wzorzec fasady:
- aby ukryć złożoność tworzonego przez siebie systemu przez dostarczenie udokumentowanego, publicznego API. Definiujemy dozwolony dostęp do API przez co redukujemy ilość możliwych przypadków błędnego użycia. Programiści muszą przyswoić sobie API fasady a nie całego systemu. Bardzo przydaje się jeżeli biblioteka jest długo rozwijana i jest duża ilość przedawnionych metod – fasada odświerza API skupiając sie jedynie na operacjach aktualnych.
- aby uprościć używanie cudzej biblioteki. Definiuje się wygodne, dostosowane do konkretnego zastosowania metody pośredniczące między systemami. Zwiększa się czytelność kodu.
- aby poprawić użyteczność biblioteki ze źle skonstruowanym API przez stworzenie nakładki, która dostarcza nowe, upoprządkowane API.
Zasada działania:
Fasada jest obiektem pośredniczącym między klasami klienta wywołującego operacji a klasami te operacje wykonującymi. Najczęściej fasade definiuję się jako Singleton, ponieważ przeważnie w systemie jest tylko jedna. Obiekt fasady wie, które klasy ukrywanego systemu są odpowiedzialne za wykonanie danego żądania i wykonuje odpowiednie wywołania do właściwych obiektów systemu. Ukrywane klasy wykonują zlecone operacje nie wiedząc nawet że jest coś takiego w systemie jak fasada.

Przykład:

 1 public class Klasa1 {
 2
 3     public void metoda1() {
 4       //jakaś funkcjonalność
 5     }
 6
 7     public void medtoda2() {
 8         //.. tutaj też coś
 9     }
10 }
 1 public class Klasa2 {
 2     public void metoda1() {
 3         //... trochę kodu
 4     }
 5
 6     public void metoda2() {
 7         //... jeszcze trochę
 8     }
 9 }
10 
 1 public class Fasada {
 2     public void metoda1() {
 3         Klasa1 k1 = new Klasa1();
 4         k1.metoda1();
 5     }
 6
 7     public void metoda2() {
 8         Klasa2 k2 = new Klasa2();
 9         k2.metoda2();
10     }
11 }
12 
  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 6 2009

Wzorce projektowe: Command

Command jest operacyjnym wzorcem projektowym, którego celem jest encapsulacja czynności do wykoniania i ich parametrów. W programach niektóre czynności mogą być wykonywane długo, np. odczytywanie rekordów z bazy danych. Klient, który wykonuje taką metodę na drugim obiekcie niekiedy musi czekać, aż wykonywanie tej metody się zakończy i dopiero wtedy może dalej wykonywać swój kod. Warto w takich sytuacjach skorzystać z wzorca Command. Wzorzec ten określa polecenie jako obiekt, który jest wysyłany od klienta do odbiorcy, co pozwala nam na takie funkcjonalności jak: kolejkowanie poleceń, kontrolowanie historii (nawet z operacją ‘undo’) i temu podobne.

We wzorcu tym uczestniczą aktorzy:
1. Interfejs Polecenie którego implementują wszystkie konkrente Polecenia, posiadający metodę ‘wykonaj’. Jeżeli potrzeba więcej funkcjonalności, można dodać metody takie jak: ‘wycofaj’ albo ‘zapisuj w logach’.
2. Konkretne polecenia, implementujące interfejs Polecenie i jego metody. W tych obiektach hermetyzowane są zadania, które muszą wykonać. Tu są również ustalane powiązania między operacjami do wykonaia a odbiorcą, który te operacje musi wykonać.
3. Klient, który jest odpowiedzialny za stworzenie Polecenia (obiektu) i ustaleniu jego odbiorcy. Następnie tak utworzone pytania zostają przekazane Obiektowi Wykonującemu
4. Obiekt wywołujący, który przechowuje utworzone przez Klienta Polecenie i w określonym momencie wywołuje metodę ‘wykonaj’
5. Odbiorca – wie w jaki sposób wykonać operacje niezbędne do realizacji określonego żądania
Klient wysyłający polecenie, Otrzymujący polecenie , Polecenie i Pośrednik transportujący polecenie od klienta do odbiorcy.

Zobaczmy:

 1 public interface Polecenie {
 2     public void wykonaj();
 3 }
 4 
 1 public class KonkretnePolecenie1 implements Polecenie{
 2
 3     ObiektRealizujący obiektRealizujący;
 4
 5     public void wykonaj() {
 6         obiektRealizujący.metoda1();
 7     }
 8
 9     public KonkretnePolecenie1(ObiektRealizujący obiektRealizujący) {
10         this.obiektRealizujący = obiektRealizujący;
11     }
12 }
13 
 1 public class KonkretnePolecenie2 implements Polecenie{
 2
 3     ObiektRealizujący obiektRealizujący;
 4
 5     public void wykonaj() {
 6         obiektRealizujący.metoda2();
 7     }
 8
 9     public KonkretnePolecenie2(ObiektRealizujący obiektRealizujący) {
10         this.obiektRealizujący = obiektRealizujący;
11     }
12 }
 1 public class ObiektWywołujący {
 2     Polecenie polecenie;
 3
 4
 5     public void ustawPolecenie(Polecenie polecenie) {
 6         this.polecenie = polecenie;
 7         System.out.println("Ustawiono polecenie: "+this.polecenie.getClass().getName());
 8     }
 9
10     public void wykonajPolecenie() {
11         System.out.println("Wykonuję polecenie: "+this.polecenie.getClass().getName());
12         polecenie.wykonaj();
13     }
14
15
16 }
17 
 1 public class ObiektRealizujący {
 2
 3     public void metoda1() {
 4         System.out.println("Wykonuję metodę 1...  ");
 5     }
 6
 7     public void metoda2() {
 8         System.out.println("Wykonuje metodę 2...");
 9     }
10 }
11 
 1 public class Klient {
 2
 3     public static void main(String[] args) {
 4         ObiektWywołujący obiektWywołujący = new ObiektWywołujący();
 5         ObiektRealizujący obiektRealizujący = new ObiektRealizujący();
 6
 7         Polecenie polecenie1 = new KonkretnePolecenie1(obiektRealizujący);
 8         Polecenie polecenie2 = new KonkretnePolecenie2(obiektRealizujący);
 9
10         obiektWywołujący.ustawPolecenie(polecenie1);
11         obiektWywołujący.wykonajPolecenie();
12
13         obiektWywołujący.ustawPolecenie(polecenie2);
14         obiektWywołujący.wykonajPolecenie();
15
16     }
17 }
18 

Diagram wzroca:

  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 4 2009

Wzorce projektowe: Decorator

Wzorzec Decorator jest wzorcem strukturalnym. Jego rolą jest nadawanie obiektowi nowych funkcjonalności ale jednocześnie zapobiec eksplozji klas, która mogłaby w takiej sytuacji wystąpić. Drugą zaletą dekoratorów jest to, że możemy zmieniać funkcjonalność dynamicznie w trakcie działania programu.
Dekoratory implementują ten sam interfejs co klasa, której funkcjonalność mają rozszerzyć i przyjmuja oryginalny obiekt jako parametr konstruktora. Wszystkie metody są delegowane do obiektu ‘wyżej’ i dodatkowo wykonywane nowe funkcjonalności.

Przykład:
Zaprojektujemy system deserów, gdzie każdy z deserów będzie mógł być przystrojony jakimś dodatkiem jak bita śmietana czy wisionka.
Najpierw budujemy interfejs Deser:

 1 public interface Deser {
 2
 3     public String getName();
 4
 5 }
 6 

Teraz tworzymy jakis deser np. Lody Waniliowe:

 1 public class LodyWaniliowe implements Deser{
 2 
 3     public String getName() {
 4 
 5         return "Lody waniliowe";
 6 
 7     }
 8 }
 9 

Dekoratory powinny implementować ten sam interfejs co klasa oryginalna. Stwórzmy więc abstrakcyjna klasę dla wszystkich dekoratorów:

 1 abstract public class DeserDekorator implements Deser {
 2     protected Deser deser;
 3
 4     protected DeserDekorator(Deser deser) {
 5         this.deser = deser;
 6     }
 7
 8 }
 9 

Teraz tworzymy dwa prawdziwe dekoratory:

 1 public class BitaŚmietana extends DeserDekorator{
 2     protected BitaŚmietana(Deser deser) {
 3         super(deser);
 4     }
 5
 6     public String getName() {
 7         return deser.getName() + " plus bita śmietana";
 8     }
 9 }
10 
 1 public class Wisionka extends DeserDekorator {
 2     protected Wisionka(Deser deser) {
 3         super(deser);
 4     }
 5
 6     public String getName() {
 7         return deser.getName() + " i wisionka";
 8     }
 9 }
10 

Metody getName() dekoratorów najpierw delegują metodę do obiektu wyżej i dodają nową funkcjonalność w postaci nazwy dekoratora.
Sprawdźmy:

 1 public class Kawiarenka {
 2     public static void main(String[] args) {
 3
 4         Deser deser = new LodyWaniliowe();
 5
 6         deser = new BitaŚmietana(deser);
 7         deser = new BitaŚmietana(deser);
 8         deser = new Wisionka(deser);
 9 
10
11         System.out.println(deser.getName());
12
13     }
14 }
15 

Wynik działania programu:

Lody waniliowe plus bita śmietana plus bita śmietana i wisionka

Diagram wzorca:

  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 3 2009

Wzorce projektowe: Prototype

Wzorzez projektowy Prototype jest konstrukcyjnym wzorcem projektowym, którego zadaniem jest unikanie powstawania nowych instancji danego obiektu. Prototype tworzy klony podanego obiektu zamiast tworzenia nowych. Jest to potrzebne gdy musimy uniknąć tworzenia w aplikacji klienckiej podklas obiektu budującego (tak jak to jest w przypadku Abstract Factory). Drugim przypadkiem kiedy Prototype jest wykorzystywany jest sytuacja, gdy potrzebujemy obiektu, którego utworzenie nowej instancji jest zbyt kosztowne.
Aby zaimplementować ten wzorzec deklaruje się klasę bazową abstrakcyjną która implementuje interfejs Cloneable. Klient który chce powołać nowy obiekt wywołuje metode clone() na prototypie, albo metodę fabrykującą z parametrem określającym pożądaną docelową klasę pochodną.
Implementacja wzorca może wyglądać następująco:

Tworzymy produkt abstrakcyjny i jego implementację:

 1 public abstract class Product implements Cloneable {
 2     private String name;
 3 
 4     @Override
 5     protected Object clone() {
 6         try {
 7
 8             Product copy = (Product) super.clone();
 9             return copy;
10
11         } catch (CloneNotSupportedException e) {
12             e.printStackTrace();
13             return null;
14         }
15     }
16
17     public String getName() {
18         return name;
19     }
20
21     public void setName(String name) {
22         this.name = name;
23     }
24 }
 1 public class ConcreteProduct extends Product {
 2 
 3 }
 4 

i budujemy klienta, który korzysta z wzorca Prototype:

 1 public class Klient {
 2     private Product product;
 3
 4     public Klient(Product product) {
 5         this.product = product;
 6     }
 7
 8     public Product makeProduct() {
 9         return (Product)product.clone();
10     }
11
12     public static void main(String[] args) {
13         Product temp = null;
14
15         Product p = new ConcreteProduct();
16         p.setName("Przykładowy Produkt");
17         //inne działania definiujące prototyp
18
19         Klient klient = new Klient(p);
20         System.out.print(p.hashCode());
21         System.out.print(" ");
22         System.out.println(p.getName());
23
24         for (int i = 0; i < 10; i++) {
25             temp = klient.makeProduct();
26             System.out.print(i + ": "+temp.hashCode());
27             System.out.print(" ");
28             System.out.println(temp.getName());
29         }
30     }
31 }

Diagram wzorca:

  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark

Feb 3 2009

Wzorce projektowe: Builder

Wzorzec projektowy Builder jest wzorcem konstrukcyjnym. Jego celem jest hermetyzacja kodu odpowiedzialnego za tworzenie obiektów od kodu klienta. Od wzorców z rodziny fabryk różni go jeszcze to, że Builder potrafi budować obiekty wg różnych algorytmów krok po kroku – Fabryki budują obiekty o zbliżonych funkcjonalnościach w jednym etapie.
Wzorzec Builder składa się: z interfejsu reprezentującego abstrakcyjnego budowniczego, klas go implementujących jako konkretni budowniczowie, Klasy Director, która zarządza procesem budowania, oraz produktem który ma zostać wyprodukowany.

Produkt który chcemy produkować:

 1 public class Drink {
 2     private String name = "";
 3     private Map<String, Integer> płyny = new HashMap<String, Integer>(); //nazwa i obj. w ml
 4     private List<String> dodatki = new ArrayList<String>();
 5
 6     public void setName(String name) {
 7         this.name = name;
 8     }
 9
10     public void addPłyn(String nazwa, int ilość) {
11         płyny.put(nazwa, ilość);
12     }
13
14     public void addDodatek(String nazwa) {
15         dodatki.add(nazwa);
16     }
17
18 }
19 

Tworzymy abstrakcyjnego Buildera:

 1 public abstract class DrinkBuilder {
 2     protected Drink drink;
 3
 4     public Drink getDrink() {
 5         return drink;
 6     }
 7
 8     public void createNewDrink() {
 9         drink = new Drink();
10     }
11
12     public abstract void ustalNazwę();
13
14     public abstract void wlejPłyny();
15
16     public abstract void dodajDodatki();
17 }
18 

oraz dwóch konkretnych budowniczych:

 1 public class HellRazorDrinkBuilder extends DrinkBuilder {
 2     public void ustalNazwę() {
 3         drink.setName("Hell Razor");
 4     }
 5
 6     public void wlejPłyny() {
 7         drink.addPłyn("Whiskey", 33);
 8         drink.addPłyn("Tequila", 33);
 9         drink.addPłyn("Wódka", 33);
10     }
11
12     public void dodajDodatki() {
13
14     }
15 }
 1 public class ArizonaSunriseDrinkBuilder extends DrinkBuilder {
 2     public void ustalNazwę() {
 3         drink.setName("Arizona Sunrise");
 4     }
 5
 6     public void wlejPłyny() {
 7         drink.addPłyn("Tequila", 45);
 8         drink.addPłyn("Sok cytrynowy", 15);
 9         drink.addPłyn("Sok pomarańczowy", 120);
10     }
11
12     public void dodajDodatki() {
13         drink.addDodatek("Grenadina");
14     }
15 }
16 

Budowniczowie budują drinki wg konkrentego przepisu.
Teraz czas na Directora u nas nazwanego Barmanem, który zarządza procesem tworzenia drinków:

 1 public class Barman {
 2     private DrinkBuilder drinkBuilder;
 3
 4     public void setDrinkBuilder(DrinkBuilder db) {
 5         this.drinkBuilder = db;
 6     }
 7
 8     public Drink getDrink() {
 9         return drinkBuilder.getDrink();
10     }
11
12     public void zróbDrinka() {
13         drinkBuilder.createNewDrink();
14         drinkBuilder.ustalNazwę();
15         drinkBuilder.wlejPłyny();
16         drinkBuilder.dodajDodatki();
17     }
18 }
19 

Rola Barmana jest tutaj bardzo ważna, w tej klasie możemy umieścić kod, który zarządza co ma być zbudowane. W naszym przypadku Barman buduje to co mu podamy, ale nie stoi nic na przykładzie dodanie np takiego kodu:
(abstrachując)

if(zamawiający == ładnaDziewczyna){
  drinkBuilder.dodajExtraDodatki();
//albo
 drinkBuilder.podajWCzystejSzklance();
}

czyli wywołanie niektórych metod buildera uzależnić od instrukcji warunkowych.

A tak nasz Budowniczy może być wykorzystywany:

 1 public class SmakoszDrinków {
 2
 3     public static void main(String[] args) {
 4
 5         Barman barman = new Barman();
 6
 7         DrinkBuilder hellRazor = new HellRazorDrinkBuilder();
 8 
 9         barman.setDrinkBuilder(hellRazor);
10         barman.zróbDrinka();
11         Drink drink = barman.getDrink();
12
13     }
14 }
15 

Diagram wzorca:

  • Wykop
  • Blip
  • Twitter
  • Facebook
  • DZone
  • Digg
  • Blinklist
  • Delicious
  • Evernote
  • LinkedIn
  • Google Bookmarks
  • Google Buzz
  • Google Reader
  • Share/Bookmark