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

Feb 2 2009

Wzorce projektowe: Singleton

Singleton jest wzorcem konstrukcyjnym, który nie pomaga przy tworzeniu obiektów lecz wręcz odwrotnie – przeszkadza. Jego zadaniem jest tworzenie obiektu, który jest tylko w jednej instacji w całej aplikacji. Często bywa że w aplikacji potrzeba coś jedynego w swoim rodzaju, np. kolejka drukowania lub zarządzanie połączeniami z bazą danych. Do tych celów nadaje się wzorzec Singleton.
Singleton posiada konstruktor prywatny co zapobiega przypadkowemu jego wywołaniu, referencję do obiektu otrzymujemy przez wywołanie statycznej metody getInstance().
Klasa wzorca singleton wygląda tak:

 1 public class Singleton {
 2     private static Singleton ourInstance;
 3
 4     public static Singleton getInstance() {
 5
 6         if (ourInstance == null) {
 7             ourInstance = new Singleton();
 8         }
 9
10         return ourInstance;
11     }
12
13     private Singleton() {
14     }
15 
16     //inne metody
17 }
18 

Przez niektórych ten wzorzec jest uznawany za antywzorzec – jest tak ponieważ Singleton w niektórych okolicznościach może działać niezgodnie z zamierzeniem.
Pierwszym takim przypadkiem jest środowisko wielowątkowe, które może spowodować równoległy dostęp do obiektu przez dwa wątki. Aby zapobiec utracie danych musimy zaopatrzyć metodę getInstance() w słowo kluczowe

 1 synchronized

. Może to trochę spowolnić dostęp do Singletona, ale zabezpieczy go przez środowiskiem wielowątkowym.
Innym sposobem jest oznaczenie zmiennej ourInstance jako finalna podczas wywoływania.
Drugim przypadkiem jest wywołanie metody przez kilka obiektów z różnych ClassLoaderów. W środowisku serwerowym kilka instancji ClassLoaderów jest zjawiskiem normalnym, więc gdy Singleton ma działać powinniśmy zabezpieczyć się w taki sposób, aby ładować Klasę Singletona zawsze z tego samego ClassLoadera. Nie jest to łatwe zadanie ale możliwe do zrealizowania, np w ten sposób:

 1     private static Class getClass(String classname)
 2             throws ClassNotFoundException {
 3         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 4         if (classLoader == null)
 5             classLoader = Singleton.class.getClassLoader();
 6         return (classLoader.loadClass(classname));
 7     }
 8 

Trzecim problemem który może zakłócić funkcje Singletona może być klonowanie jego. Jest to możliwe gdy implementuje interfejs Cloneable (lub dziedziczy po klasie, która go implementuje). W takiej sytuacji musimy nadpisać metodę clone(), która nie pozwoli na klonowanie Singletona.

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

Feb 2 2009

Wzorce Projektowe: Observer

Wzorzec Observer jest wzorcem operacyjnym. Definiuje relację jeden do wielu między obiektami, gdzie obiekty obserwujące zostają powiadomione o zmianie stanu obiektu obserwowanego. Obiekty obserwujące nie muszą wiedzieć, że istnieją inne obiekty obserwujące to samo, oraz same mogą decydować czy chcą być powiadamiane o zmianach stanu czy też nie.

Aby zrealizować takie zadanie wymagane jest utworzenie dwóch interfejsów: Obserwator i Obserwowany. Pierwszy ma zdefiniowaną metodę, która jest wykonywana gdy obiekt obserwowany zmieni stan. Interfejs obiektu obserwowanego musi posiadać trzy metody: getter i setter dla obserwatorów oraz metodę powiadamiającą wszystkich zarejestrowanych obserwatorów o zmianie stanu.
Wyobraźmy sobie że chcemy dostawać informację o każdej zmianie w serwisie WWW. Za wyświetlanie danych na stronie jest odpowiedzialna klasa SiteRenderer, która metodą updateContent dodaje nowy materiał do treści strony.

 1 public class SiteRenderer {
 2
 3     // ...
 4 
 5     public void updateContent() {
 6 
 7         //kod aktualizujący wygląd witryny
 8 
 9     }
10 
11     // ...
12 }
13 

Chcemy być informowani o zmienie treści serwisu, więc klasa SiteRenderer będzie naszym obserwowanym obiektem. Tworzymy dwa interfejsy: Obserwator i Obserwowany:

 1 public interface Obserwator {
 2
 3     public void aktualizuj();
 4
 5 }
 6 
 1 public interface Obserwowany {
 2
 3     public void dodajObserwatora(Obserwator o);
 4
 5     public void usuńObserwatora(Obserwator o);
 6
 7     public void powiadomWszystkich();
 8 }
 9 

Implementujemy interfejs obserwowanego do SiteRenderer:

 1 public class SiteRenderer implements Obserwowany{
 2
 3     private List<Obserwator> obsewatorzy = new ArrayList<Obserwator>();
 4     // ...
 5
 6     public void updateContent() {
 7
 8         //kod aktualizujący wygląd witryny
 9         powiadomWszystkich();
10     }
11
12     // ...
13     public void dodajObserwatora(Obserwator o) {
14         obsewatorzy.add(o);
15     }
16
17     public void usuńObserwatora(Obserwator o) {
18         obsewatorzy.remove(o);
19     }
20
21     public void powiadomWszystkich() {
22         for (Obserwator obs : obsewatorzy) {
23             obs.aktualizuj();
24         }
25     }
26 }
27 

Teraz każdy obiekt który ma być poinformowany o zmianie stanu obiektu obserwowanego musi dopisać się do jego listy obserwatorów, a obserwator będzie go o każdej zmianie informował poprzez wywołanie metody aktualizuj();

Diagram wzroca:

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