Oct 27 2009

Java, TMPDIR i MacOS X

Prawie cały dzień spędziłem na bezefektywnej próbie uruchomienia starego jBossa wygrzebanego z backup’u na moim Macu. Po dłuższych analizach znalazłem przycznę. Otóż, podczas startu, serwer ustawiał zmienną java.io.tmpdir. W systemie MacOS X w takim momenie zostaje utworzony nowy folder w /private/var/folders.Problemem jest to, że w nazwie tego folderu występują znaki plus +, które przez niektóre mechanizmy (np. Xerces) są traktowane jako spacje. Obejściem tego problemu jest ustawienie parametru java.io.tmpdir w JAVA_OPTS. JAVA_OPTS="-Djava.io.tmpdir=/tmp/". Zmienna ta w przypadku serwera jBoss jest ustawiana w pliku run.conf, więc należy go edytować i dopisać co podałem.

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

Jul 2 2009

Projekt Euler

Ostatnio szperając po sieci, natrafiłem na ciekawy aczkolwiek nie nowy projekt. Aż dziwne, że nie znalazłem go do tej pory. Projekt Euler bo o nim mowa przedstawia problemy, z którymi musimy się zmierzyć jako programiści. Problemy ułożone na granicy matematyki, kombinatoryki czy tam innej “yki” nie są w możliwe do rozwiązania bez komputera (no chyba, że przez jakiegoś geniusza) Jako programiści mamy zadanie napisać program, który nam to zadanie rozwiąże. Sam projekt nie ocenia jakości naszego kodu bo podajemy tylko wynik i ten jest sprawdzany. Wiadomo jednak, że najlepiej jest się uczyć od innych albo na gotowych zadaniach, więc dla mnie projekt to jest strzał w 10-tkę. Zwłaszcza, że każdy problem (obecnie jest ich ponad 250) ma oddzielny topic na forum i można z innymi programistami podzielić się uwagami i wspólnie zoptymalizować kod, do najkrótszego/najlepszego. Co prawda w javie programuję, już prawie 10 lat, ale akurat zacząłem pisać kod w Ruby’m więc projekt będzie jak znalazł do potrenowania nowych technik.

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

Feb 28 2009

dobry kod – na skróty

Każdy kiedyś uczył się pisać. Najpierw rysując szlaczki aby opanować kontrolę nad dłonią, potem literka za literką cały alfabet. Gdy już się nauczyliśmy – charakter pisma wyrabiał się wraz z ilością zapisanego tekstu. Jednak niektórzy starali stawiać równe, okrągłe literki, starali się robić równe odstępy między wyrazami. Tak długo, aż mieli ładny, czytelny charakter pisma. Ale nie wszyscy się starają – większość pisze tak jak leci.

Tak samo jest z programistami. Gdy nauczą się podstaw programowania – nabierają doświadczenia poprzez pisanie programów i…. to wszystko. Pisanie programów w stylu: „ważne, że działa” i doświadczenie jedynie poprzez własnoręcznie napisany kod. Ale nie każdy taki jest. Niektórzy chcą pisać dobry kod – starają się, uczą nowych rzeczy, analizują inne programy, aby zobaczyć jak piszą inni. Dążą do pisania kodu w jak najlepszym stylu.

Jak jednak wygląda dobry kod? Jak się go pisze? Czterowersem z rymem naprzemiennym?

Jak można ocenić, że kod jest dobry?

Przez tyle lat gdy na świecie są komputery narodziło się mnóstwo autorytetów z dziedziny programowania. Powstały też kanony, standardy i przede wszystkim dobre style programowania. Po tych wszystkich latach można także ocenić który kod jest dobrze napisany.

Przede wszystkim program powinien być bardziej przemyślany niż napisany. Czym więcej czasu poświęcimy na projektowanie i analizę, tym więcej czasu zaoszczędzimy na programowaniu.

Przed przystąpieniem do pracy na programem powinniśmy wiedzieć co, gdzie i jak powinno w programie funkcjonować. Które klasy powinny znaleźć się w programie, co powinny wykonywać i jak się nazywać, a także z którymi innymi klasami powinny być połączone.

Na samym początku powinniśmy zadać sobie pytanie CO ten program (lub pojedynczy moduł programu) powinien robić lub CZYM powinien być. Musimy się zastanowić jakie warunki program musi spełniać aby dobrze działał – czyli wykonywał to co powinien. Pamiętaj: niezależnie gdzie pracujesz, jakie oprogramowanie tworzysz oraz jakiego języka programowania używasz – jednego możesz być pewien: wymagania się zmieniają. Dobrze napisany program pozwoli nam na szybkie zmodyfikowanie i dostosowanie programu do nowych wymagań.

Powinniśmy napisać przypadki użycia. Są to napisane krok po kroku działania programu. Może to bardzo pomóc przy projektowaniu programu. Analizując później tak sporządzony przypadek użycia – możemy rzeczowniki w nim użyte uznać jako klasy a czasowniki jako metody.

Nazwy klas powinny jednoznacznie wskazywać czym klasa jest, a nazwy metod – jakie czynności wykonują. Każdy obiekt reprezentuje jedno pojęcie (przynajmniej powinien). Nie używa się obiektów realizujących dwa lub więcej obowiązków. Jeżeli przeglądając właściwości obiektu znajdujemy właściwości o wartości null lub w ogóle nie wykorzystywane, to może oznaczać, że obiekt pełni kilka ról. Jeżeli takie właściwości istnieją, a jesteśmy pewni, że obiekt pełni tylko jedną rolę w systemie to nieużywane właściwości powinniśmy wywalić do oddzielnej klasy. Cały kod który może ulec zmianie oddzielamy od tego który nie ulegnie zmianie. Takie działanie nazywa się hermetyzacją.

Jeżeli wydzielimy to, co może się zmienić, a potem faktycznie się zmieni – a zmiany które to spowoduje będziemy musieli dokonać w klasach innych niż wyodrębniona – to oznacza, że hermetyzacja jest źle wykonana.

Obiekt powinien mieć tylko jedno zadanie a wszystkie usługi powinny się koncentrować na jego realizacji. Bardzo łatwo sprawdzić czy ta zasada jest zachowana. Wystarczy dla każdej metody utworzyć zdanie gdzie nazwa metody jest czasownikiem, nazwa klasy podmiotem a obiekty podane jako parametry metody mogą służyć jako dodatkowe rzeczowniki (chyba że parametr jest nieistotny bo np. tylko zmienia ilość lub szyk). Gdy metoda jest bez parametrów dodajemy „się”(może być w domyśle, chodzi o to, że metoda wykonuje czynność na samym sobie). Jeżeli utworzone w ten sposób zdanie łamie sens jego rzeczywistego odpowiednika to zasada jednej odpowiedzialności jest złamana. Np. klasa Obiad posiadająca metody: liczbaKalorii(), podajSkład(), ugotuj(). Porównując klasę do rzeczywistego obiektu obiad wiemy, że obiad jest w stanie podać SWOJĄ liczbę kalorii albo SWÓJ skład, ale SAM SIĘ NIE ugotuje. Więc metoda ugotuj() łamie tę zasadę. Obiad gotuje kucharz, czyli Kucharz.ugotuj(Obiad).

Analiza ta jednak w dużej mierze zależy od znajomości systemu. Np. w powyższym przykładzie metody Kucharz.ugotuj(Obiad) jest zgodna z zasadą jednej odpowiedzialności. A metoda Kucharz.ugotuj()? Czy jest zgodna? Na pierwszy rzut oka nie jest. Nie ma logicznego sensu aby kucharz SAM SIEBIE gotował. Jednak system może być zaprojektowany tak, że każdy obiad ma swojego kucharza i obiekt Obiad jest przekazywana obiektowi Kucharz jako parametr konstruktora. Zatem metoda ugotuj() wie, którego Obiadu użyć i nie potrzebuje go jako parametru. Mając tą wiedzę – czy dalej twierdzimy, że metoda Kucharz.ugotuj() łamie zasadę jednej odpowiedzialności? Pamiętaj, że ocena o odpowiednim stosowaniu zasad ( lub ich łamaniu) jest wysoce zależna od znajomości systemu.

W naszym programie powinniśmy także odpowiednio zadbać o relacje między klasami. Rodzajów tychże nie ma dużo: dziedziczenie, implementacja lub asocjacja.

Z dziedziczeniem jest pewien problem. Dziedziczenie (czy też zgodnie z UML – generalizacja) powinno być wykonane w ten sposób aby była możliwość podstawienia typów pochodnych w miejsce ich typów bazowych (Zasada paradygmatu Liskov’a). Dziedziczenie w celu rozszerzenia funkcjonalności niesie za sobą ryzyko, że klasy pochodne nie będą potrzebne do ich działania i nie będą potrafiły tych metod zaadoptować. W takich sytuacjach należy poszukać innych rozwiązań, np. delegacja(jedna z grupy asoocjacji) lub kompozycja.

Kompozycja to jest używanie interfejsów. Z tym, że nie chodzi tylko o słowo kluczowe interface ale także klasy abstrakcyjne. Kompozycja pozwala stosować zachowanie udostępnione przez rodzinę innych klas i zmieniać to zachowanie w trakcie działania. Określamy w obiekcie, którym interfejsem jesteśmy zainteresowani (który realizuje niezbędne operacje) a w jego miejsce podsyłać klasy, które ten interfejs implementują. Stosują kompozycję nie jesteśmy uzależnieni od konkretnej implementacji. Kod jest elastyczny na rozszerzanie bo można utworzyć nową klasę o tym interfejsie i ją podstawić. Używając interfejsów a nie klas które je implementują pozwoli na napisanie kodu dla klas, które nawet jeszcze nie istnieją. Kompozycja jest dobrym sposobem aby program był otwarty na rozszerzanie i podatny na zmiany, co jest bardzo ważne: powinniśmy starać się powodować aby raz napisany kod nie był modyfikowany. Jednocześnie napisać go tak aby szło go rozszerzyć, jeżeli nie przez kompozycję to np. przez dziedziczenie.

Nie ma lepszego sposobu na sprawdzenie czy pogram jest odporny na zmiany niż sama zmiana.

Innym typem relacji jest asocjacja, która oznacza zależność jednej klasy od drugiej. Utworzenie obiektu klasy A w obiekcie klasy B jest asocjacją. Są dwa główne rodzaje asocjacji: delegacja i agregacja. W delegacji obiekt który ma wykonać pewną czynność nie wykonuje jej, ale przekazuje jej wykonanie do innej klasy. W agregacji jeden obiekt jest częścią drugiego, np. obiekt Adres jest częścią obiektu Pracownik.

Częstym problemem jest nie odróżnienie kiedy używać agregacji a kiedy kompozycji. Agregację wykorzystujemy wtedy, gdy obiekt agregowany istnieje poza obiektem agregującym, jako oddzielny byt. Jeżeli obiekt agregowany nie musi mieć własnego bytu – używamy kompozycji.

Kolejną ważną zasadą jest unikanie powtarzania kodu. Najłatwiej tego dokonać poprzez wyodrębnienie wspólnych fragmentów i umieszczenie ich w jednym miejscu. Czyni to kod łatwiejszym w utrzymaniu i przy ewentualnych zmianach. Chodzi tu także o to, aby nie powtarzać funkcjonalności w kilku miejscach. W takim przypadku trzeba przenieść kod dotyczący tej funkcjonalności w jedno miejsce. Jednym słowem: należy się starać, aby każda informacja i operacja była tylko w jednym miejscu.

Pracując z dużym programem koncentruj się tylko na jednej możliwości naraz. Nie pozwól się zdekoncentrować i nie zajmuj się innymi możliwościami. Najpierw należy się skupiać na głównych funkcjonalnościach programu. Potem – wchodząc w głąb – programujemy kolejne funkcjonalności jedną po drugiej tak długo, aż będą napisane wszystkie z tego poziomu. Potem piszemy bardziej szczegółowe i tak dalej.

Drugą szkołą jest skupianie się na przypadkach użycia. Tworzymy scenariusz przejścia przez aplikację i piszemy kod aby ten scenariusz obsłużyć. Potem kolejny (inny) scenariusz i kod. Tak długo aż scenariuszami opiszemy wszystkie funkcjonalności programu.

Opanowanie tych zasad pomoże zaznajomienie się z wzorcami projektowymi – co i moim zdaniem jest wielce zalecane.

Mam nadzieję, że teraz pisany kod będzie prześliczny a jeszcze bardziej elastyczny :D – po prostu dobry kod.

  • 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