Aug 30 2010

Wzorce Projektowe, źródło książkowe

Znajomość wzorców projektowych zdaje się być jedną z ważniejszych umiejętności posiadanych przez wprawnego programistę. Dlaczego jest zatem tak, że nie każdy programista je zna? Składa się na to kilka współczynników. Po pierwsze: wzorce wymagają pewnego doświadczenia programistycznego, aby podczas nauki można było znaleźć dla nich praktyczne zastosowanie. Po drugie: oryginalna książka bandy czworga jest śmiertelnie nudna i nauka wzorców z tej książki wymaga solidnego samozaparcia. Te czynniki sprawiają, że nawet jak wzorce są omawiane na studiach, to studenci w większym stopniu o nich zapominają. Zostaje zatem samonauczanie podczas swojej pracy – poznając wzorce szukamy dla nich praktycznego zastosowania w obecnym projekcie.

Gdzie jednak szukać dobrego źródła na ten temat? Jeżeli szukamy wśród pozycji książkowych to godną polecenia pozycją jest: Head First Design Patterns. Mimo, iż jest to chyba jedyna pozycja na polskim rynku dotycząca tego tematu, to zdecydowanie jest godna polecenia. Książka pisana w dość luźnym stylu wyjaśnia stosowanie wzorców na praktycznych przykładach. Budując krok po kroku fragmenty kodu czytelnik widzi te same funkcjonalności zarówno z wykorzystaniem wzorców jak i bez nich. Jest to bardzo dużą zaletą książki, bo początkowi programiści mają największy problem ze znalezieniem miejsca na wzorzec w swoich projektach. Po lekturze tej książki na pewno będzie to łatwiejsze.
Pamiętając, że wzorców jest razem 23 – 14tu z nim zostały poświęcone całe, dość spore rozdziały. Pozostałe 9 zostało opisane dość skrótowo, co nie znaczy, że nie zrozumiale. Uważam jednak, że wzorzec Proxy ze względu na swoją specyfikę i dość rzadkie zastosowanie powinien być potraktowany opisowo, natomiast rozdział ten powinien zostać poświęcony na wzorzec Builder albo Chains of responsibility. Być może jednak jest to tylko moje odczucie.
Zdecydowanie polecam tą książkę zarówno tym, którzy nie potrafią wzorców jak i tym, którym się zdaje że je potrafią. Książka potrafi ukazać problem z całkowicie innej perspektywy. Bardzo łatwo z nią nauczyć się lub utrwalić wiedzę na temat wzorców.
hfdepa

Jeżeli nauka wzorców ma być tylko jednym z kroków do nauki poprawnego programowania obiektowego to z tego miejsca polecam drugą książkę z tej serii: Head First Object-Oriented Analysis and Design. Obie pozycje w duecie dostarczają bardzo duży zasób wiedzy z programowania obiektowego.

Obie pozycje zdecydowanie polecam,

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

Feb 12 2009

Wzorce projektowe: Visitor

W aplikacjach często wykorzystuje się kolekcje takie jak List, Set, Map czy pochodne. W kolekcjach tych przetrzymuje się obiekty różnego typu. Nie jednokrotnie także buduje się kolekcje o budowie hierarchicznej, np. za pomocą wzorca Composite. Są sytuajcje, gdy musimy przeiterować się po takiej kolekcji w jakimś celu i wykonać na każdym obiekcie jakąś metodę. Np. w sytuacji przeindeksowania, lub przywrócenia utraconych danych. Problem w tym, że kolekcja jak wspomniano może zawierać kilka różnych obiektów i każdy powinien być traktowany inaczej. Korzystanie w takiej sytuacji z instanceof nie jest do końca zgodne z myślą projektowania obiektowego. Zatem co? Zatem wzorzec Viisitor.
Visitor jest wzorcem operacyjnym, który potrafi wykonać daną operację na całej strukturze obiektów. Visitor pozwala na zdefiniowanie nowych operacji na elementach struktury, bez zmiany klas tych elementów.
We wzorcu mamy aktorów:
Visitor: interfejs który definiuje metody dla wszystkich odwiedzanych obiektów. metody powinny być przeciążane aby miały taką samę nazwę. Wybór metody zależny jest od obiektu który jest podany jako parametr.
ConcreteVisitor – implementacja interfejsu Visitor. Każdy ConcreteVisitor jest budowany dla różnego rodzaju operacji, możemy w systemie zbudować kilka Visitorów i każdy będzie do innego celu.
Visitable – abstakcja – interfejs który jest implementowany przez obiekty, które mają być wizytowane. każdy obiekt kolekcji powinien implementować ten interfejs.
ConcreteVisitable – Klasy implementujące interfejs Visitable definiujące metodę accept(). Visitor poprzez tą metode dostaje się do wnętrza obiektu wizytowanego.
ObjectStruture – Klasa zawierająca wszystki obiekty, które moga być odwiedzone. Klasa udostępnia iterator, który pozwala na przemieszczanie sie między kolejnymi składnikami kolekcji. ObjectStructure może być klasą lub całą strukturą klas jak np. Composite.

Sprawdźmy:

Posiadamy sklep internetowy: Część struktury składa się z następujących elementów:
GrupaKlientów, Klient, Zamówienie i Przedmiot. Każde zamówienie posiada co najmniej jedne przedmiot. Każdy klient posiada conajmniej jedno zamówienie.
Chcemy wygenerować raporty dotyczące grup użytkowników naszego sklepu.

Tworzym dwa interfejsy : Visitor i Visitable.
Musimy utworzyć klasę zbierającą dane (ZbieraczDanych), która będzie Visitorem.
Klasy GrupaKlientów, Klient, Zamówienie i Przedmiot muszą implementować interfejs Visitable.
Klasa kliencka która zarządza całością.

Sprawdźmy jak to wygląda:

 1 public interface Visitor {
 2     public void visit(Klient klient);
 3
 4     public void visit(Zamówienie zamówienie);
 5
 6     public void visit(Przedmiot przedmiot);
 7 }
 8 
1 public interface Visitable {
2     public void accept(Visitor visitor);
3 }
4 
 1 public class GrupaKlientów implements Visitable {
 2     private List<Klient> klienci = new ArrayList<Klient>();
 3
 4     public void accept(Visitor visitor) {
 5         for (Iterator it = klienci.iterator(); it.hasNext();) {
 6             ((Klient) it.next()).accept(visitor);
 7         }
 8     }
 9
10     public void addKlient(Klient klient) {
11         klienci.add(klient);
12     }
13 }
14 
 1 public class Klient implements Visitable {
 2     private String name;
 3     private List<Zamówienie> zamówienia = new ArrayList<Zamówienie>();
 4
 5
 6     public Klient(String name) {
 7         this.name = name;
 8     }
 9
10     public void dodajZamówienie(Zamówienie zamówienie) {
11         zamówienia.add(zamówienie);
12     }
13
14     public void accept(Visitor visitor) {
15         visitor.visit(this);
16         for (Iterator it = zamówienia.iterator(); it.hasNext();) {
17             ((Zamówienie) it.next()).accept(visitor);
18         }
19
20     }
21
22     public void setName(String name) {
23         this.name = name;
24     }
25
26     public String getName() {
27         return name;
28
29     }
30 }
31 
 1 public class Zamówienie implements Visitable {
 2     private String name;
 3     private List<Przedmiot> przedmioty = new ArrayList<Przedmiot>();
 4
 5     public Zamówienie(String name) {
 6         this.name = name;
 7     }
 8
 9     public Zamówienie(String name, String przedmiot) {
10         this.name = name;
11         this.dodajPrzedmiot(new Przedmiot(przedmiot));
12     }
13
14     public void setName(String name) {
15         this.name = name;
16     }
17
18     public void accept(Visitor visitor) {
19
20         visitor.visit(this);
21         for (Iterator it = przedmioty.iterator(); it.hasNext();) {
22             ((Przedmiot) it.next()).accept(visitor);
23         }
24     }
25
26     public String getName() {
27         return name;
28     }
29
30     public void dodajPrzedmiot(Przedmiot przedmiot) {
31         przedmioty.add(przedmiot);
32     }
33 }
34 
 1 public class Przedmiot implements Visitable {
 2     private String name;
 3
 4     public Przedmiot(String name) {
 5         this.name = name;
 6     }
 7
 8
 9     public void accept(Visitor visitor) {
10         visitor.visit(this);
11     }
12
13     public String getName() {
14         return name;
15     }
16
17     public void setName(String name) {
18         this.name = name;
19     }
20 }
21 
 1 public class ZbieraczDanych implements Visitor {
 2     private int liczbaKlientów;
 3     private int liczbaZamówień;
 4     private int liczbaPrzedmiotów;
 5
 6
 7     public void visit(Klient klient) {
 8         System.out.println(klient.getName());
 9         liczbaKlientów++;
10     }
11
12     public void visit(Zamówienie zamówienie) {
13         System.out.println(zamówienie.getName());
14         liczbaZamówień++;
15     }
16
17     public void visit(Przedmiot przedmiot) {
18         System.out.println(przedmiot.getName());
19         liczbaPrzedmiotów++;
20
21     }
22
23     public void pokażWyniki() {
24         System.out.println("Liczba klientów:" + liczbaKlientów);
25         System.out.println("Liczba zamówień:" + liczbaZamówień);
26         System.out.println("Liczba przedmiotów: " + liczbaPrzedmiotów);
27     }
28 }
29 
 1 public class VisitorTester {
 2     public static void main(String[] args) {
 3         Klient klient1 = new Klient("Klient 1");
 4
 5         klient1.dodajZamówienie(new Zamówienie("zamówienie_1", "przedmiot_1"));
 6         klient1.dodajZamówienie(new Zamówienie("zamówienie_2", "przedmiot_1"));
 7         klient1.dodajZamówienie(new Zamówienie("zamówienie_3", "przedmiot_1"));
 8
 9         Klient klient2 = new Klient("Klient 2");
10         Zamówienie zamówienie = new Zamówienie("zamówienie_a");
11         zamówienie.dodajPrzedmiot(new Przedmiot("przedmiot_a1"));
12         zamówienie.dodajPrzedmiot(new Przedmiot("przedmiot_a2"));
13         zamówienie.dodajPrzedmiot(new Przedmiot("przedmiot_a3"));
14         klient2.dodajZamówienie(zamówienie);
15         klient2.dodajZamówienie(new Zamówienie("zamówienie_b", "przedmiot_b1"));
16
17         GrupaKlientów grupaKlientów = new GrupaKlientów();
18         grupaKlientów.addKlient(klient1);
19         grupaKlientów.addKlient(klient2);
20         ZbieraczDanych zbieraczDanych = new ZbieraczDanych();
21
22         grupaKlientów.accept(zbieraczDanych);
23
24         zbieraczDanych.pokażWyniki();
25
26     }
27 }
28 

Diagram wzorca:

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

Feb 11 2009

Wzorce projektowe: Interpreter

Interpreter jest wzorcem projektowym, którego zadaniem jest interpretacja poleceń innego języka. Dany język rokładany jest na części gramatyczne i potem na zorientowaną obiektowo hierarchię. Interpreter nie jest właściwie niczym innym jak wzorcem Kompozyt, tylko że ma trochę inną rolę – reprezentuje reguły gramatyczne. W niektórych przypadkach wzorzec Interpreter może zwiększyć funkcjonalność wzorca Kompozyt. Częstym przypadkiem używania wzorca jest interpretacja zasad.

Interpreter składa się :
Context: który przetrzymuje dane, które powinny poddać się interpretacji,
Abstract Expression: klasa abstrakcyjna która interpretuje polecenia,
…. Expression – konkretne klasy, które interpretują treść Contextu dla poszczególnych przypadków.
Abstract Expression i konkretne implementacje tworza wzorzec Template Method.

Main (albo inaczej Client)

Program do interpretacji liczby rzymskiej (znaleziony gdzieś na notatkach, ale nie mojego autorstwa):

Context: przechowuje treść tekstu do interpretacji:

 1 public class Context {
 2
 3     private String input;
 4     private int output;
 5
 6     public Context(String input) {
 7         this.input = input;
 8     }
 9
10     public String getInput() {
11         return input;
12     }
13
14     public void setInput(String input) {
15         this.input = input;
16     }
17
18     public int getOutput() {
19         return output;
20     }
21
22     public void setOutput(int output) {
23         this.output = output;
24     }
25
26 }
27 

Expression: interpretuje fragment cyfry rzymksiej jako decymalny odpowiednik, expression jest implemntowany przez cztery podklasy: Thousand…, Hundred…, Ten…, i OneExpression które interpretują poszczególne części cyfry.

 1 public abstract class Expression {
 2
 3     public void interpret(Context context) {
 4         if (context.getInput().length() == 0)
 5             return;
 6
 7         if (context.getInput().startsWith(nine())) {
 8             context.setOutput(context.getOutput() + (9 * multiplier()));
 9             context.setInput(context.getInput().substring(2));
10         } else if (context.getInput().startsWith(four())) {
11             context.setOutput(context.getOutput() + (4 * multiplier()));
12             context.setInput(context.getInput().substring(2));
13         } else if (context.getInput().startsWith(five())) {
14             context.setOutput(context.getOutput() + (5 * multiplier()));
15             context.setInput(context.getInput().substring(1));
16         }
17
18         while (context.getInput().startsWith(one())) {
19             context.setOutput(context.getOutput() + (1 * multiplier()));
20             context.setInput(context.getInput().substring(1));
21         }
22     }
23
24     public abstract String one();
25
26     public abstract String four();
27
28     public abstract String five();
29
30     public abstract String nine();
31
32     public abstract int multiplier();
33
34 }
 1 public class ThousandExpression  extends Expression{
 2
 3     public String one() { return "M"; }
 4     public String four(){ return " "; }
 5     public String five(){ return " "; }
 6     public String nine(){ return " "; }
 7     public int multiplier() { return 1000; }
 8 }
1 public class HundredExpression extends Expression{
2     public  String one() { return "C"; }
3     public  String four(){ return "CD"; }
4     public  String five(){ return "D"; }
5     public  String nine(){ return "CM"; }
6     public  int multiplier() { return 100; }
7 }
1 public class TenExpression  extends Expression{
2     public String one() { return "X"; }
3     public String four(){ return "XL"; }
4     public String five(){ return "L"; }
5     public String nine(){ return "XC"; }
6     public int multiplier() { return 10; }
7 }
1 public class OneExpression  extends Expression{
2     public String one() { return "I"; }
3     public String four(){ return "IV"; }
4     public String five(){ return "V"; }
5     public String nine(){ return "IX"; }
6     public int multiplier() { return 1; }
7 }

Main: wykonawca interpretacji:

 1 public class MainInterpreter {
 2
 3 	/**
 4 	 * @param args
 5 	 */
 6 	public static void main(String[] args) {
 7 
 8 	      String roman = "MCMXXVIII";
 9 	      Context context = new Context(roman);
10
11 	      // Build the 'parse tree'
12 	      ArrayList<Expression> tree = new ArrayList<Expression>();
13 	      tree.add(new ThousandExpression());
14 	      tree.add(new HundredExpression());
15 	      tree.add(new TenExpression());
16 	      tree.add(new OneExpression());
17
18 	      // Interpret
19 	      for (Iterator it = tree.iterator(); it.hasNext();)
20 	      {
21 	    	  Expression exp = (Expression)it.next();
22 	    	  exp.interpret(context);
23 	      }
24
25 	      System.out.println(roman + " = " + Integer.toString(context.getOutput()));
26 	}
27 }
28 

Diagram wzorca:

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

Feb 11 2009

Wzorce projektowe: Memento

Memento jest wzorcem, którego wykorzystuje w sytuacji gdy musimy zamrozić stan obiektu w celu jego późniejszego wykonania. Na przykład tak działa Undo. Drugim częstym sposobem na wykorzystanie wzorca memento jest zapamiętywanie wyników zapytań bazy danych w celu ich ponownego późniejszego wykoniania. Zwłaszcza gdy wykonanie zapytań jest bardzo zasobożerne.

Wzorzec memento składa się z trzech głównych obiektów:
Originator: klasa, której stan ma być przechowywany, musimy w niej implementować metody zapisujące i odczytujące stan z obiektu Memento
Memento: klasa, w która potrafi przechować wszystkie dane z Originatora, które muszą być utrwalone.
Caretaker: jest klasą która przechowuje kolekcję memento i udostępnia metody do zapisu i odczytu.

Przypuśmy że mamy klasę (Originator), która ma pole State pole to musi być utrwalane. Tworzymy zatem drugą klasę (Memento) która również posiada pole State. W momencie gdy potrzebujemy utrwalenia wywołujemy metodę saveToMemento Originatora. Metoda ta tworzy obiekt Memento z obecną wartością pola state Originatora. następnie nowo utworzone Memento jest wysyłane do Caretakera w celu utrwalenia.

Zobaczmy:

 1 public class Memento {
 2     private String state;
 3
 4     public String getSavedState() {
 5         return state;
 6     }
 7
 8     public Memento(String stateToSave) {
 9         state = stateToSave;
10     }
11 }
12 
 1 public class Originator {
 2     private String state;
 3
 4     public void setState(String state) {
 5         System.out.println("Originator: Setting state to: " + state);
 6         this.state = state;
 7     }
 8
 9     public Memento saveToMemento() {
10         System.out.println("Originator: Saving to Memento");
11         return new Memento(state);
12     }
13
14     public void restoreFromMemento(Memento m) {
15         state = m.getSavedState();
16         System.out.println("Originator: State restored from Memento: " + state);
17     }
18 }
19 
 1 public class Caretaker {
 2     private List<Memento> savedStates = new ArrayList<Memento>();
 3
 4     public void addMemento(Memento m) {
 5         savedStates.add(m);
 6     }
 7
 8     public Memento getMemento(int index) {
 9         return savedStates.get(index);
10
11     }
12 }
13 
 1 public class MementoRunner {
 2     public static void main(String[] args) {
 3         Caretaker caretaker = new Caretaker();
 4
 5         Originator originator = new Originator();
 6
 7         originator.setState("State 1");
 8
 9         originator.setState("State 2");
10         caretaker.addMemento(originator.saveToMemento());
11         originator.setState("State 3");
12         caretaker.addMemento(originator.saveToMemento());
13         originator.setState("State 4");
14         originator.restoreFromMemento(caretaker.getMemento(1));
15 
16
17     }
18 }
19 

Diagram wzorca:

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

Feb 10 2009

Wzorce projektowe: Mediator

Wzorzec operacyjny Mediator jest wykorzystywany do skupiania złożonych procedur komunikacji i sterowania w środowisku powiązanych obiektów. Obiekty w systemie zamiast komunikować się między sobą bezpośrednio robią to poprzez klasę Mediatora – nie muszą wtedy wiedzieć o swoim własnym istnieniu bezpośrednio. Wysyłają informację do mediatora, a on przekaże go do obiektu, który ma być celem żądania. Pozwala to na łatwą przyszłą modyfikację aplikacji, ponieważ wszystkie wpólne relacje są w jednym miejscu.

Sprawdźmy działanie programu, który oddziela obiekt wysyłający informacje, od obiektu który jest odbiorcą tych wiadomości.

 1 public class Mediator {
 2     private boolean slotFull = false;
 3
 4     private int number;
 5
 6     public synchronized void storeMessage(int num) {
 7         while (slotFull == true) {
 8             try {
 9                 wait();
10             } catch (InterruptedException e) {
11                 e.printStackTrace();
12             }
13         }
14
15         slotFull = true;
16         number = num;
17         notifyAll();
18
19     }
20
21     public synchronized int retrieveMessage() {
22
23         while (slotFull == false)         
24
25             try {
26                 wait();
27             } catch (InterruptedException e) {
28             }
29
30         slotFull = false;
31
32         notifyAll();
33
34         return number;
35
36     }
37 }
38 
 1 class Producer extends Thread {
 2
 3     private Mediator med;
 4
 5     private int id;
 6
 7     private static int num = 1;
 8
 9     public Producer(Mediator m) {
10         med = m;
11         id = num++;
12     }
13
14     public void run() {
15
16         int num;
17
18         while (true) {
19
20             med.storeMessage(num = (int) (Math.random() * 100));
21
22             System.out.println("p" + id + "-" + num + "   ");
23
24         }
25     }
26 }
 1 class Consumer extends Thread {
 2
 3     private Mediator med;                
 4
 5     private int id;                 
 6
 7     private static int num = 1;
 8
 9     public Consumer(Mediator m) {
10         med = m;
11         id = num++;
12     }
13
14     public void run() {
15
16         while (true) {
17
18             System.out.println("c" + id + "-" + med.retrieveMessage() + "   ");
19
20         }
21     }
22 }
 1 class MediatorDemo {
 2
 3     public static void main(String[] args) {
 4
 5         Mediator mb = new Mediator();
 6
 7         new Producer(mb).start();
 8
 9         new Producer(mb).start();
10
11         new Consumer(mb).start();
12
13         new Consumer(mb).start();
14
15         new Consumer(mb).start();
16
17         new Consumer(mb).start();
18
19     }
20 }

Diagram wzorca:

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

Feb 9 2009

Wzorce projektowe: Chain of Responsibility

Wzorzec projektowy Chains of Responsibility prowadzi do utworzenia łańcucha obiektów, które analizują żadanie. Analiza przeprowadzana jest kolejno przez każdy z nich. Obiekt może zapewnić obsługę żądania lub przekazuje to żądanie dalej, lub to i to.

 1 public class Żądanie {
 2     private String title;
 3     private int value;
 4
 5     public Żądanie(String title, int value) {
 6         this.title = title;
 7         this.value = value;
 8     }
 9
10     public void print() {
11         System.out.println(title + " " + value);
12     }
13
14     public int getValue() {
15         return value;
16     }
17 }
18 
 1 public abstract class Łapacz {
 2     protected Łapacz następca;
 3
 4     public void setNastępca(Łapacz łapacz) {
 5         następca = łapacz;
 6     }
 7
 8     public abstract void łapŻądanie(Żądanie żądanie);
 9 }
10 
 1 public class ŁapaczNegatywny extends Łapacz {
 2
 3     public void łapŻądanie(Żądanie żądanie) {
 4         if (żądanie.getValue() < 0) {
 5             System.out.println("Łapacz negatywny łapie wartości mniejsze niż 0");
 6             żądanie.print();
 7         } else {
 8             if (następca != null)
 9                 następca.łapŻądanie(żądanie);
10         }
11     }
12 }
13 
 1 public class ŁapaczPozytywny extends Łapacz {
 2
 3     public void łapŻądanie(Żądanie żądanie) {
 4         if (żądanie.getValue() > 0) {
 5             System.out.println("Łapacz pozytywny łapie wartości większe niż 0");
 6             żądanie.print();
 7         } else {
 8             if (następca != null)
 9                 następca.łapŻądanie(żądanie);
10         }
11     }
12 }
 1 public class ŁapaczNeutralny extends Łapacz {
 2
 3     public void łapŻądanie(Żądanie żądanie) {
 4         if (żądanie.getValue() == 0) {
 5             System.out.println("Łapacz neutralny łapie wartości równe 0");
 6             żądanie.print();
 7         } else {
 8             if (następca != null)
 9                 następca.łapŻądanie(żądanie);
10         }
11     }
12 }
 1 public class TesterŁapaczy {
 2     public static void main(String[] args) {
 3         Łapacz łapacz1 = new ŁapaczNegatywny();
 4         Łapacz łapacz2 = new ŁapaczPozytywny();
 5         Łapacz łapacz3 = new ŁapaczNeutralny();
 6
 7         łapacz1.setNastępca(łapacz2);
 8         łapacz2.setNastępca(łapacz3);
 9
10         łapacz1.łapŻądanie(new Żądanie("Wartość negatywna ", -5));
11         łapacz1.łapŻądanie(new Żądanie("Wartość pozytywna ", 1));
12         łapacz1.łapŻądanie(new Żądanie("Wartość negatywna ", -1));
13         łapacz1.łapŻądanie(new Żądanie("Wartość negatywna ", -2));
14         łapacz1.łapŻądanie(new Żądanie("Wartość neutralna ", 0));
15         łapacz1.łapŻądanie(new Żądanie("Wartość negatywna ", -1));
16         łapacz1.łapŻądanie(new Żądanie("Wartość pozytywna ", 1));
17         łapacz1.łapŻądanie(new Żądanie("Wartość neutralna ", 0));
18         łapacz1.łapŻądanie(new Żądanie("Wartość pozytywna ", 1));
19         łapacz1.łapŻądanie(new Żądanie("Wartość negatywna ", -1));
20     }
21 }
22 

Diagram wzorca:

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

Feb 9 2009

Wzorce projektowe: Iterator

Iterator jest operacyjnym wzorcem projektowym, którym zapewnia metodę dostępu sekwencyjnego do elementów obiektu zagregowanego bez ujawniania jego reprezentacji wewnętrznej.
Obecnie od czasu java 5 i pojawieniu się typów generycznych wzorzec iteratora stracił na wadze. Typy generyczne pełnią kontrolę nad zwracanymi obiektami. Każda kolekcja też, posiada metodę iterator, która zwraca jej iterator. Jeżeli chcemy więc użyć własnego, najlepiej w tym celu udekorować kolekcję, dodając funkcjonalności do iteratora.
Przykład iteratora:

 1 public interface Iterator {
 2     public boolean hasNext();
 3
 4     public Object next();
 5 }
 6 
 1 public interface Containter {
 2     public Iterator getIterator();
 3 }
 4 
 1 class BooksCollection implements Containter
 2 {
 3 	private String titles[] = {"Design Patterns","1","2","3","4"};
 4
 5     public Iterator getIterator()
 6 	{
 7 		BookIterator result = new BookIterator();
 8 		return result;
 9 	}
10
11
12 	private class BookIterator implements Iterator
13 	{
14 		private int position;
15
16         public boolean hasNext()
17 		{
18 			if (position < titles.length)
19 				return true;
20 			else
21 				return false;
22 		}
23 		public Object next()
24 		{
25 			if (this.hasNext())
26 				return titles[position++];
27 			else
28 				return null;
29 		}
30 	}
31 }

Diagram wzorca:

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

Feb 9 2009

Wzorce projektowe: Flyweight

Flyweight jest strukturalnym wzorcem projektowym, którego zadaniem jest zmniejszenie ilości instancji klas w aplikacji. Jest bardzo zbliżony do Singleton’a i często Singleton jest wykorzystywany we wzorcu Flyweight.
Czasami zdaża się, że potrzebujemy na prawdę wielkiej ilości małych obiektów. Np. rysując zawartość folderu, możemy stworzyć obiekt katalogu lub pliku dla każdego obecnego w systemie. Może to doprowadzić do niepotrzebnego zuzycia zasobów. Te obiekty tak na prawdę nie różnią się wiele, ikonka taka sama tylko nazwa inna.
W takiej sytuacji wykorzystuje się wzorzec Flywieght. Jego zadaniem jest stworzenie jednego obiektu, którego dane są delegowane do innego miejsca. Zamiast tworzyć kilkaset małych obiektów, tworzy się jeden, a jako paramter podaje się to co jest zmienne. Tworzy się jedną instancję dla rodzaju obiektu. Więc np, folder i folder zaznaczony (który ma inną ikonkę) to dwa różne obiekty.

Spójrzmy na przykład:
Potrzebujemy narysować serię przycisków. Stworzyliśmy klasę FlyweightButton, która za pomocą metody draw() rysuje je na ekranie. Przyciski są podobne, różnią sie tylko nazwą więc trzeba będzie ograniczyć liczbę instancji w systemie do minimum. Jako pomocnika napisaliśmy klasę FlyweightButtonManager, która zarządza pulą przycisków. Akurat w każdym rzędzie przycisk ma inną ikonkę więc dla każdego rzędu jest wywoływana nowa instancja klasy. Działanie managera jest podobne do działania singletona. gdy wywoływana jest metoda getFlyweightButton() sprawdza się czy dla danego rzędu FlyweightButton juz instnieje, jeżeli nie – to tworzy się nowy – potem zwraca się go.
zobaczmy:

 1 public class FlyweightButton {
 2     private int label;
 3
 4     public FlyweightButton(int label) {
 5         this.label = label;
 6         System.out.println("Rodzina Flyweight Buttonów:" + label);
 7     }
 8
 9     public void draw(int num) {
10         System.out.println(label + " " + num + " " + this.hashCode());
11     }
12 }
13 
 1 public class FlyweightButtonManager {
 2     private FlyweightButton[] pool;
 3
 4     public FlyweightButtonManager(int maxRows) {
 5         pool = new FlyweightButton[maxRows];
 6
 7     }
 8
 9     public FlyweightButton getFlyweightButton(int col) {
10         if (pool[col] == null) { //sprawdzamy czy Button już jest
11             pool[col] = new FlyweightButton(col);// jak nie to tworzymy nowy
12         }
13         return pool[col];// i go zwracamy
14     }
15
16 }
17 
 1 public class FlyweightDemo {
 2     public static final int ROWS = 6, COLS = 10;
 3
 4     public static void main(String[] args) {
 5         FlyweightButtonManager manager = new FlyweightButtonManager(ROWS);
 6         for (int i = 0; i < ROWS; i++) {
 7             for (int j = 0; j < COLS; j++) {
 8                 manager.getFlyweightButton(i).draw(j);
 9             }
10         }
11     }
12 }
13 

Dla uwidocznienia w naszym przykładzie umieściłem kod, który drukuje HashCode aby upewnić się, że wszystkie wywołane Buttony są tej samej instancji.

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: 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