WZORCE PROJEKTOWE Jzyki i Techniki Programowania 7 XI

  • Slides: 39
Download presentation
WZORCE PROJEKTOWE Języki i Techniki Programowania 7. XI. 2003 Joanna Zięba

WZORCE PROJEKTOWE Języki i Techniki Programowania 7. XI. 2003 Joanna Zięba

CZYM SĄ WZORCE PROJEKTOWE? „Wzorzec opisuje problem powtarzający się w środowisku naszego systemu i

CZYM SĄ WZORCE PROJEKTOWE? „Wzorzec opisuje problem powtarzający się w środowisku naszego systemu i opisuje jego rozwiązanie w taki sposób, że może ono być wykorzystane wielokrotnie i na różne sposoby” (Huston Design Patterns home. earthlink. net/~huston 2/dp/patterns. ht ml)

ANALIZA WZORCÓW Należy wiedzieć, gdzie wzorców szukać (literatura, zasoby internetowe itp. ), umieć je

ANALIZA WZORCÓW Należy wiedzieć, gdzie wzorców szukać (literatura, zasoby internetowe itp. ), umieć je rozpoznawać w programach innych i wybierać najlepsze do swoich celów. Przy wyborze warto zwrócić uwagę na następujące aspekty zagadnienia: - kontekst (co, gdzie, kiedy. . . ) - warunki wstępne - konsekwencje użycia danego rozwiązania - możliwe alternatywy (inne wzorce lub rezygnacja z ich stosowania na rzecz własnych rozwiązań)

KLASYFIKACJA WZORCÓW (konstrukcyjne, strukturalne, behawioralne)

KLASYFIKACJA WZORCÓW (konstrukcyjne, strukturalne, behawioralne)

WZORCE KONSTRUKCYJNE (creational patterns) Opisują jak obiekt może zostać stworzony. Powinny wyodrębniać szczegóły kreacji

WZORCE KONSTRUKCYJNE (creational patterns) Opisują jak obiekt może zostać stworzony. Powinny wyodrębniać szczegóły kreacji obiektów tak aby kod był niezależny od ich typów i nie musiał być zmieniany w miarę pojawiania się nowych rodzajów obiektów. Przykłady: Singleton, Fabryka Abstrakcyjna, Prototyp, Budowniczy

SINGLETON (Singleton) Gwarantuje, że dana klasa ma tylko jeden obiekt (instancję) i zapewnić globalny

SINGLETON (Singleton) Gwarantuje, że dana klasa ma tylko jeden obiekt (instancję) i zapewnić globalny sposób dostępu do tego obiektu. Obiekt stworzony wg tego wzorca może zastąpić zmienną globalną. PODEJŚCIE: Klasę typu Singleton należy uczynić odpowiedzialną za tworzenie, inicjalizację, dostęp i ew. zmiany obiektów. Sam obiekt musi być jej składnikiem typu private static, a funkcja inicjalizacji i dostępu – public static

Prosty przykład: class Singleton { private Singleton s; private int i; private Singleton(int x)

Prosty przykład: class Singleton { private Singleton s; private int i; private Singleton(int x) { i = x; } public static Singleton get. Reference() { if (s == null) s = new Singleton(2); return s; } public int get. Value() { return i; } public void set. Value(int x) { i = x; } } Przykład z życia: „prezydent RP” – istnieje tylko co najwyżej jeden, a nazwa jednoznacznie identyfikuje sprawującą urząd osobę.

 • Inne wzorce, jak Fabryka Abstrakcyjna, Budowniczy i Prototyp mogą używać Singletona w

• Inne wzorce, jak Fabryka Abstrakcyjna, Budowniczy i Prototyp mogą używać Singletona w swojej implementacji • Singletonami są często obiekty typu Fasada (zwykle potrzeba tyko jednej) oraz Stan (powinien przyjmować jedną wartość)

FABRYKA (Simple Factory) Wzorzec ten w zależności od dostarczonych danych, zwraca instancję jednej z

FABRYKA (Simple Factory) Wzorzec ten w zależności od dostarczonych danych, zwraca instancję jednej z możliwych klas. Najczęściej zwracane klasy wywodzą się z tej samej klasy podstawowej i mają takie same metody, ale każda z nich wykonuje swoje zadania w inny sposób i jest zoptymalizowana dla innego rodzaju danych. Rozwinięciem tego wzorca jest fabryka polimorficzna – struktura gdzie istnieje klasa - fabryka bazowa i jej różne podklasy – specyficzne fabryki.

FABRYKA – SCHEMAT

FABRYKA – SCHEMAT

abstract class Shape { public abstract void draw(); public abstract void erase(); public static

abstract class Shape { public abstract void draw(); public abstract void erase(); public static Shape factory(String type) { if(type. equals("Circle")) return new Circle(); if(type. equals("Square")) return new Square(); throw new Runtime. Exception( "Bad shape creation: " + type); } } class Circle extends Shape { Circle() {} // Package-access constructor public void draw() { System. out. println("Circle. dra w"); } public void erase() { System. out. println("Circle. era se"); } } public class Shape. Factory 1 extends Test. Case { String shlist[] = { "Circle", "Square", "Circle", "Square" }; List shapes = new Array. List(); public void test() { Iterator it = Arrays. as. List(shlist). iterator (); while(it. has. Next()) shapes. add(Shape. factory((Stri ng)it. next())); it = shapes. iterator(); while(it. has. Next()) { Shape s = (Shape)it. next(); s. draw(); s. erase(); } }

FABRYKA ABSTRAKCYJNA (Abstract Factory) Wzorca projektowego Abstract Factory można używać w celu otrzymania jednej

FABRYKA ABSTRAKCYJNA (Abstract Factory) Wzorca projektowego Abstract Factory można używać w celu otrzymania jednej z wielu związanych ze sobą klas obiektów, z których każdy może na żądanie zwrócić wiele innych obiektów. Wzorzec ten jest fabryką, która zwraca jedną z wielu grup klas. Można nawet gdy używa się Simple Factory, decydować, którą klasę z tej grupy zwrócić. Można także wykorzystać w implementacji wzorzec Prototype, jeśli często będą tworzone obiekty o bardzo zbliżonych własnościach.

FABRYKA ABSTRAKCYJNA – SCHEMAT

FABRYKA ABSTRAKCYJNA – SCHEMAT

PROTOTYP (Prototype) W tym wzorcu tworzenie obiektu polega na modyfikacji uprzednio utworzonej kopii pewnego

PROTOTYP (Prototype) W tym wzorcu tworzenie obiektu polega na modyfikacji uprzednio utworzonej kopii pewnego obiektu - prototypu. Zastosowanie tego wzorca jest w szczególności uzasadnione, gdy tworzone obiekty zawiera dużą ilość czasochłonnie stworzonych danych, których tylko mała część jest modyfikowana względem prototypu. Przykład z życia: klonowanie, podział komórki

interface Xyz { Xyz cloan(); class Tom implements Xyz { public Xyz cloan() {

interface Xyz { Xyz cloan(); class Tom implements Xyz { public Xyz cloan() { return new Tom(); } public String to. String() { return "ttt"; } } class Dick implements Xyz { public Xyz cloan() { return new Dick(); } public String to. String() { return "ddd"; } } class Factory { private java. util. Map prototypes; public Factory(){ prototypes = new java. util. Hash. Map(); prototypes. put( "tom", new Tom() ); prototypes. put( "dick", new Dick() ); public Xyz make. Object( String s ) { return ((Xyz)prototypes. get(s)). cloan(); } } public static void main( String[] args ) { for (int i=0; i < args. length; i++) System. out. print( (new Factory()). make. Object( args[i] ) + " " );

BUDOWNICZY (Builder) Ten wzorzec jest podobny do wzorca Abstrakcyjnej Fabryki, gdyż służy on tworzeniu

BUDOWNICZY (Builder) Ten wzorzec jest podobny do wzorca Abstrakcyjnej Fabryki, gdyż służy on tworzeniu zbioru obiektów, ale tutaj tworzone obiekty są z sobą powiązane (w szczególności gdy tworzą Kompozyt). Obiekt złożony jest tworzony na podstawie danych wejściowych. Dane te są przetwarzane tak, że kolejne ich części tworzą komponenty, które z kolei mogą służyć wraz z innymi danymi utworzeniu obiektu złożonego.

BUDOWNICZY – SCHEMAT

BUDOWNICZY – SCHEMAT

WZORCE STRUKTURALNE (structural patterns) Pozwalają łączyć obiekty w większe struktury, mając zastosowanie np. w

WZORCE STRUKTURALNE (structural patterns) Pozwalają łączyć obiekty w większe struktury, mając zastosowanie np. w implementacji złożonego interfejsu użytkownika. Przykłady: Adapter, Most, Kompozyt, Dekorator, Fasada, Waga Piórkowa, Proxy

ADAPTER (Adapter) Wzorzec Adapter konwertuje interfejs jednej klasy na interfejs innej klasy. Używamy tego

ADAPTER (Adapter) Wzorzec Adapter konwertuje interfejs jednej klasy na interfejs innej klasy. Używamy tego wzorca, jeśli chcemy, żeby dwie niezwiązane ze sobą klasy współpracowały ze sobą w jednym programie. Koncepcja wzorca Adaptera jest bardzo prosta: piszemy klasę, która posiada wymagany interfejs, a następnie zapewniamy jej komunikację z klasą, która ma inny interfejs. Istnieją dwa sposoby realizacji: poprzez dziedziczenie i poprzez kompozycję.

ADAPTER – PRZYKŁAD IMPLEMENTACJI package adapter; import junit. framework. *; class Target { public

ADAPTER – PRZYKŁAD IMPLEMENTACJI package adapter; import junit. framework. *; class Target { public void request() {} } class Adaptee { public void specific. Request() { System. out. println("Adaptee: Specific. Request"); } } class Adapter extends Target { private Adaptee adaptee; public Adapter(Adaptee a) { adaptee = a; } public void request() { adaptee. specific. Request(); } } public class Simple. Adapter extends Test. Case { Adaptee a = new Adaptee(); Target t = new Adapter(a); public void test() { t. request(); } public static void main(String args[]) { junit. textui. Test. Runner. run(Si mple. Adapter. class); }

KOMPOZYT (Composite) Wzorzec kompozytu pozwala na jednolite traktowanie komponentów i obiektów z nich złożonych

KOMPOZYT (Composite) Wzorzec kompozytu pozwala na jednolite traktowanie komponentów i obiektów z nich złożonych poprzez specyfikację ich wspólnego interfejsu. Przykład z życia: zapis działania matematycznego, składa się ono z liczb, symboli operatorów i nawiasów; także przepis kuchenny, jeśli za komponenty uznamy poszczególne składniki.

KOMPOZYT – SCHEMAT

KOMPOZYT – SCHEMAT

Przykład: package composite; import java. util. *; import junit. framework. *; interface Component {

Przykład: package composite; import java. util. *; import junit. framework. *; interface Component { void operation(); } class Leaf implements Component { private String name; public Leaf(String name) { this. name = name; } public String to. String () { return name; } public void operation() { System. out. println(this); } } class Node extends Array. List implements Component { private String name; public Node(String name) { this. name = name; } public String to. String () { return name; } public void operation() { System. out. println(this); for(Iterator it = iterator(); it. has. Next(); ) ((Component)it. next()). operation(); } } public class Composite. Structure extends Test. Case { public void test() { Node root = new Node("root"); root. add(new Leaf("Leaf 1")); Node c 2 = new Node("Node 1"); c 2. add(new Leaf("Leaf 2")); c 2. add(new Leaf("Leaf 3")); root. add(c 2); c 2 = new Node("Node 2"); c 2. add(new Leaf("Leaf 4")); c 2. add(new Leaf("Leaf 5")); root. add(c 2); root. operation(); } public static void main (String args[]) { junit. textui. Test. Runner. run(Composi te. Structure. class); }

DEKORATOR (Decorator) Wzorzec ten pozwala na dekorowanie zachowania klasy, czyli zmianę jej funkcjonalności bez

DEKORATOR (Decorator) Wzorzec ten pozwala na dekorowanie zachowania klasy, czyli zmianę jej funkcjonalności bez potrzeby dziedziczenia, które mogłoby stworzyć zbyt wiele mało elastycznych klas. Najprostsza sytuacja:

FASADA (Facade) Często program podczas tworzenia ewoluuje i rośnie stopień jego komplikacji. Możemy zauważyć

FASADA (Facade) Często program podczas tworzenia ewoluuje i rośnie stopień jego komplikacji. Możemy zauważyć że oprócz korzyści wzorce mają też ujemną cechę: czasami generują one bardzo wiele dodatkowych klas, przez co trudniej jest zrozumieć działanie programu. Poza tym programy często składają się z szeregu podsystemów, z których każdy posiada swój własny skomplikowany interfejs. Dlatego też warto wprowadzić Fasadę – ujednolicony interfejs do szeregu interfejsów poszczególnych podsystemów.

FASADA - SCHEMAT

FASADA - SCHEMAT

WAGA PIÓRKOWA (Flyweight) Waga Piórkowa ogranicza ilość tworzonych instancji obiektów, przez przeniesienie części danych

WAGA PIÓRKOWA (Flyweight) Waga Piórkowa ogranicza ilość tworzonych instancji obiektów, przez przeniesienie części danych z stanu obiektu do parametrów metod co umożliwia ich współdzielenie. Takie rozwiązanie wpływa korzystnie na szybkość wykonywania się programu – niekiedy niekontrolowane powstawanie zbyt dużej ilości obiektów spowalnia jego pracę.

Przykład: zamknięcie wielu obiektów w jednym (przetwarzanie ich osobno skrajnie nieefektywne z powodu dużej

Przykład: zamknięcie wielu obiektów w jednym (przetwarzanie ich osobno skrajnie nieefektywne z powodu dużej ich ilości) class Data. Point { private static int count = 0; private int id = count++; private int i; private float f; public int get. I() { return i; } public void set. I(int i) { this. i = i; } public float get. F() { return f; } public void set. F(float f) { this. f = f; } public String to. String() { return "id: " + id + ", i = " + i + ", f = " + f; } } public class Many. Objects { static final int size = 1000000; public static void main(String[] args) { Data. Point[] array = new Data. Point[size]; for(int i = 0; i < array. length; i++) array[i] = new Data. Point(); for(int i = 0; i < array. length; i++) { Data. Point dp = array[i]; dp. set. I(dp. get. I() + 1); dp. set. F(47. 0 f); } System. out. println(array[size -1]); }

Rozwiązanie: zamknięcie wszystkich Data. Point w jednym Externalized. Data public static void set. F

Rozwiązanie: zamknięcie wszystkich Data. Point w jednym Externalized. Data public static void set. F (int obnum, class Externalized. Data { • static final int size = 5000000; • static int[] id = new int[size]; • static int[] i = new int[size]; • static float[] f = new float[size]; • static { • for(int i = 0; i < size; i++) • id[i] = i; • } • • class Fly. Point { • private Fly. Point() {} • public static int get. I (int obnum) { • return Externalized. Data. i[obnum]; • } • public static void set. I (int obnum, int i) { • Externalized. Data. i[obnum] = i; • } • public static float get. F (int obnum) { • return Externalized. Data. f[obnum]; • } • • • • • • float f) { Externalized. Data. f[obnum] = f; } public static String str (int obnum) { return "id: " + Externalized. Data. id[obnum] + ", i = " + Externalized. Data. i[obnum] + ", f = " + Externalized. Data. f[obnum]; } } public class Fly. Weight. Objects { public static void main (String[] args) { for(int i = 0; i < Externalized. Data. size; i++) { Fly. Point. set. I(i, Fly. Point. get. I(i) + 1); Fly. Point. set. F(i, 47. 0 f); } System. out. println( Fly. Point. str(Externalized. Data. size -1)); }

WZORCE OPERACYJNE (behavioral patterns) pomagają zdefiniować komunikację pomiędzy obiektami oraz kontrolować przepływ danych w

WZORCE OPERACYJNE (behavioral patterns) pomagają zdefiniować komunikację pomiędzy obiektami oraz kontrolować przepływ danych w złożonym programie. Przykłady: Iterator, Łańcuch Odpowiedzialności, Interpreter, Stan, Mediator, Obserwator, Memento, Strategia

STAN (State) Wzorzec Stan pozwala obiektowi zmienić zachowanie gdy zmieni się jego stan wewnętrzny.

STAN (State) Wzorzec Stan pozwala obiektowi zmienić zachowanie gdy zmieni się jego stan wewnętrzny.

Przykład (bajkowy : -)) //: state: Kissing. Princess. java package state; import junit. framework.

Przykład (bajkowy : -)) //: state: Kissing. Princess. java package state; import junit. framework. *; class Creature { private boolean is. Frog = true; public void greet() { if(is. Frog) System. out. println("Ribbet!"); else System. out. println("Darling!"); } public void kiss() { is. Frog = false; } } public class Kissing. Princess extends Test. Case { Creature creature = new Creature(); public void test() { creature. greet(); creature. kiss(); creature. greet(); } public static void main(String args[]) { junit. textui. Test. Runner. run(Ki ssing. Princess. class); }

OBSERWATOR (Observer) Wzorzec ma na celu zdefiniowanie zależności miedzy obiektami typu jeden-do-wielu tak, aby

OBSERWATOR (Observer) Wzorzec ma na celu zdefiniowanie zależności miedzy obiektami typu jeden-do-wielu tak, aby przy zmianie stanu jednego pozostałe były o tym powiadamiane i też zmieniały swój stan.

ITERATOR (Iterator) Wzorzec Iteratora jest jednym z prostszych i najczęściej wykorzystywanych. Pozwala przemieszczać się

ITERATOR (Iterator) Wzorzec Iteratora jest jednym z prostszych i najczęściej wykorzystywanych. Pozwala przemieszczać się poprzez listę lub dowolną kolejkę danych, z wykorzystaniem standardowego interfejsu, bez potrzeby znajomości wewnętrznej reprezentacji danych. Można też zdefiniować specjalne iteratory, które dokonują specjalnego przetwarzania i zwracają tylko niektóre elementy kolekcji danych.

Przykład użycia: public class Typed. Iterator implements Iterator { private Iterator imp; private Class

Przykład użycia: public class Typed. Iterator implements Iterator { private Iterator imp; private Class type; public Typed. Iterator(Iterator it, Class type) { imp = it; this. type = type; } public boolean has. Next() { return imp. has. Next(); } public void remove() { imp. remove(); } public Object next() { Object obj = imp. next(); if(!type. is. Instance(obj)) throw new Class. Cast. Exception( "Typed. Iterator for type " + type + " encountered type: " + obj. get. Class()); return obj; }

Schemat innego zastosowania (C++) <listiterator. h> class List. Iterator { public: List. Iterator(const List&

Schemat innego zastosowania (C++) <listiterator. h> class List. Iterator { public: List. Iterator(const List& alist); virtual ~List. Iterator() {} int At. End() const; void Restart(); int get. Position()const {return position; } protected: virtual List. Element* get(); virtual List. Iterator& advance(); List. Iterator& operator=(const List. Iterator& other); List. Iterator& operator=(const List& alist); private: List. Element* start; List. Element* cursor; int position; }; <Object. List. Iterator. h> class Object. List. Iterator: public List. Iterator { public: Object. List. Iterator(const Object. List& alist); virtual ~Object. List. Iterator() {} virtual Object. List. Iterator & operator++(); // advances a cursor virtual Object. List. Iterator & operator++(int); //same Object* Get(); //gets a pointer to an object void Set(Object*newobj=0); //sets a pointer to an object Object. List. Iterator&operator=(const Object. List& alist); };

WZORCE - KORZYŚCI + Sukces jest ważniejszy niż tworzenie wszędzie czegoś nowego + Dobrze

WZORCE - KORZYŚCI + Sukces jest ważniejszy niż tworzenie wszędzie czegoś nowego + Dobrze napisane wzorce ułatwiają komunikację między autorami kodu + Pochodzą z praktycznego doświadczenia i rozwiązują konkretne problemy + Wzorce wcale nie służą eliminacji programistów!

ŹRÓDŁA I BIBLIOGRAFIA • Gamma i inni (Go. F): „Elements of Reusable Object-Oriented Software”

ŹRÓDŁA I BIBLIOGRAFIA • Gamma i inni (Go. F): „Elements of Reusable Object-Oriented Software” • James William Cooper „Java. Wzorce projektowe” (wyd. Helion) • Bruce Eckel „Thinking in Patterns”, dostępne na www. bruceeckel. com • Żródła internetowe – m. in. tutoriale na www. ibm. com (Java Patterns 101 i 201)

MOTTO „Użycie wzorców projektowych pozwala nam uczyć się na sukcesach innych zamiast na własnych

MOTTO „Użycie wzorców projektowych pozwala nam uczyć się na sukcesach innych zamiast na własnych błędach”