Zaproszenie do złożenia oferty cenowej
Transkrypt
Zaproszenie do złożenia oferty cenowej
08.09.2013 09:00 Typy obiektowe Klasy definiowane w bazach danych. Zawierają atrybuty i metody. Typy obiektowe są czasem nazywane typami definiowanymi przez użytkownika. Podczas tworzenie typu obiektowego należy stworzyć: • specyfikację typu obiektowego, • ciało typu obiektowego. Specyfikacja typu obiektowego Uproszczona składnia specyfikacji typu obiektowego: CREATE [OR REPLACE] TYPE [schemat.]nazwa_typu [AUTHID {CURRENT_USER|DEFINER}] AS OBJECT ( atrybut1 typ_danych, [atrybut2 typ_danych,] [metoda1] [metoda2]); / Ciało typu obiektowego Uproszczona składnia ciała typu obiektowego: CREATE [OR REPLACE] TYPE BODY [schemat.]nazwa_typu {IS | AS} [STATIC | MEMBER] PROCEDURE ciało_procedury [STATIC | MEMBER | CONSTRUCTOR] FUNCTION ciało_funkcji [MAP | ORDER] MEMBER FUNCTION ciało_funkcji / Atrybuty: • musi być co najmniej jeden atrybut, • atrybuty muszą znajdować się przed metodami, • typ atrybutu: – dowolny typ bazy danych oprócz: ROWID, UROWID, LONG, LONG RAW, NCHAR, NCLOB, NVARCHAR2, – typy PL/SQL oprócz BINARY_INTEGER, BOOLEAN, PLS_INTEGER, RECORD, REF CURSOR, – typ wbudowany, – typ zdefiniowany przez użytkownika, • nie można stosować ograniczenia NOT NULL, • nie można używać wartości domyślnych. SELF wskazuje na aktualny obiekt (odpowiednik this w Javie). Metody [STATIC | MEMBER] PROCEDURE specyfikacja_procedury [STATIC | MEMBER | CONSTRUCTOR] FUNCTION specyfikacja_funkcji [MAP | ORDER] MEMBER FUNCTION ciało_funkcji, deklaracja_dyrektywy Metody: • składowe (MEMBER), • statyczne (STATIC ), • konstruktory (CONSTRUCTOR) – dla każdego typu obiektowego istnieje predefiniowany konstruktor o takiej samej nazwie i atrybutach jak typ. Przykład CREATE OR REPLACE TYPE zespolona AS OBJECT ( re NUMBER(5,2), im NUMBER(5,2), http://localhost:8888/html/files/typy%20obiektowe.txt Strona 1 z 39 08.09.2013 09:00 CONSTRUCTOR FUNCTION zespolona(re NUMBER, im NUMBER) RETURN SELF AS RESULT, CONSTRUCTOR FUNCTION zespolona(re NUMBER) RETURN SELF AS RESULT, MEMBER PROCEDURE dodaj (z zespolona), MEMBER PROCEDURE drukuj ); / Porównywanie i sortowanie SET SERVEROUTPUT ON DECLARE z1 zespolona := zespolona(3,4); z2 zespolona := zespolona(6); BEGIN DBMS_OUTPUT.ENABLE; IF(z1 < z2) THEN DBMS_OUTPUT.PUT_LINE('z1 < z2'); ELSIF(z1 > z2) THEN DBMS_OUTPUT.PUT_LINE('z1 > z2'); ELSE DBMS_OUTPUT.PUT_LINE('z1 = z2'); END IF; END; / Metody służące do porównywania i sortowania: • MAP – służy do przekształcenia obiekt na typ umożliwiający sortowanie, zwraca DATE, NUMBER, VARCHAR2, CHAR, REAL, jeżeli używane jest dziedziczenie metoda MAP może wystąpić w typach pochodnych tylko jeżeli występowała w typie bazowym, • ORDER – przyjmuje jeden parametr typu obiektowego i zwraca wartość typu NUMBER, wartość zwracana jest wynikiem porównania obiektu przekazanego jako parametr z danym obiektem, jeżeli używane jest dziedziczenie metoda ORDER musi znajdować się w typie bazowym, a typy pochodne nie mogą jej przesłaniać. Dziedziczenie Słowa kluczowe związane z dziedziczeniem: • UNDER nazwa_typu_bazowego – dziedziczenie, • FINAL – nie można dziedziczyć po typie obiektowym (domyślnie), • NOT FINAL – można dziedziczyć po typie obiektowym, • INSTANTIABLE – można tworzyć obiektów danego typu obiektowego (domyślnie), • NOT INSTANTIABLE – nie można tworzyć obiektów danego typu obiektowego, • OVERRIDING – przesłonięcie metody z typu bazowego. Tabele obiektowe Uproszczona składania tworzenia tabeli obiektowej: CREATE TABLE nazwa_tabeli OF nazwa_typu_obiektowego; Widoki obiektowe create_view::= CREATE [OR REPLACE] [[NO] FORCE] [EDITIONING] VIEW [schema.] view [ ( { alias [ inline_constraint... ] http://localhost:8888/html/files/typy%20obiektowe.txt Strona 2 z 39 08.09.2013 09:00 | out_of_line_constraint } [, { alias [ inline_constraint...] | out_of_line_constraint } ] ) | object_view_clause | XMLType_view_clause ] AS subquery [ subquery_restriction_clause ] ; Kolumna obiektowa column_properties::= { object_type_col_properties | nested_table_col_properties | { varray_col_properties | LOB_storage_clause } [ (LOB_partition_storage [, LOB_partition_storage ]...) ] | XMLType_column_properties }... Funkcje i operatory związane z obiektami : • REF - wskaźnik obiektów w tabelach i widokach obiektowych, • DEREF - zwraca obiekt, na który wskazuje wartość REF, • IS DANGLING - zwraca prawdę jeżeli REF nie wskazuje na żaden obiekt, • TREAT - sprawdza w czasie wykonania programu, czy obiekt typ bazowy można potraktować jako obiekt jednego z typów pochodnych, • IS OF - zwraca prawdę jeżeli dany obiekt jest określonego typu, • VALUE - zwraca obiekt z tabeli i widoków obiektowych. Diagram klas „Diagramy klas [27] wykorzystują klasy i interfejsy w celu przedstawienia szczegółów dotyczących składających się na system elementów oraz zachodzących między nimi powiązań statycznych.” Widoczność Widoczność [27]: + publiczna: „Jeżeli chcemy, aby atrybut lub operacja były dostępne bezpośrednio dla dowolnej innej klasy, należy zadeklarować je jako publiczne”, # chroniona: „Elementy zadeklarowane jako chronione mogą być używane przez metody będące częścią danej klasy, jako również przez metody dowolnej innej, która ją dziedziczy.” ~ pakietowa: „Jeżeli do klasy dodamy atrybut lub operację zadeklarowaną przy użyciu pakietowego poziomu widoczności, wtedy bezpośredni dostęp do tego niego/niej mają wszystkie klasy w tym samym pakiecie.” – prywatna: „Jedynie klasa, która zawiera element oznaczony jako prywatny, może mieć dostęp do danych umieszczonych w prywatnym atrybucie lub też wywoływać prywatną operację” Atrybuty: • wpisane (ang. inlined), • w postaci powiązań z innymi klasami. http://localhost:8888/html/files/typy%20obiektowe.txt Strona 3 z 39 08.09.2013 09:00 Związki pomiędzy klasami: • zależność, • asocjacja, • agregacja (agregacja częściowa), • agregacja całkowita (agregacja częściowa, kompozycja, złożenie), • dziedziczenie (uogólnienie, generalizacja). http://brasil.cel.agh.edu.pl/~09sbfraczek/diagram-klas,1,11.html Przypadek użycia Zasady dotyczące określenia zakresu przypadków użycia: • „Przypadek użycia musi być zainicjalizowany przez aktora.” • „Kiedy przypadek użycia zostanie uznany za wykonany, to nie można już do niego nic przekazywać ani niczego od niego otrzymywać. Żądana funkcjonalność została wykonana lub wystąpił błąd.” • „Po zakończeniu działania przypadku użycia system znajduje się w stanie pozwalającym na ponowne uruchomienia tego przypadku użycia lub wystąpił błąd. Style przedstawiania przypadków użycia: • w pełni sformatowany, • nieformalny, • tabela jednokolumnowa, • tabela dwukolumnowa, • styl Rational Unified Process. "Szablon w pełni sformatowanego przypadku użycia <nazwa> <nazwa powinna być celem w postaci krótkiego aktywnego wyrażenia czasownikowego> Kontekst użycia: <dłuższe określenie celu; jeśli są potrzebne normalne warunki wystąpienia> Zakres: <zakres projektowy; jaki system jest traktowany jako projektowana czarna skrzynka> Poziom: <jeden z: streszczenie, cel użytkownika, podfunkcja> Aktor główny: <nazwa roli dla aktora głównego lub jej opis> Uczestnicy i interesy: <lista uczestników i ich kluczowych interesów w tym przypadku użycia> Warunek początkowy: <jakiego stanu świata oczekujemy na wstępie> Minimalna gwarancja: <w jaki sposób interesy są chronione przy dowolnym zakończeniu> Gwarancja powodzenia: <stan świata, gdy cel będzie zrealizowany> Wyzwalacz: <co uruchamia przypadek użycia; może być zdarzenie zegarowe> Główny scenariusz powodzenia: <umieść tu kroki scenariusza od wyzwalacza do realizacji celu i całe późniejsze sprzątanie> <numer kroku> <opis akcji> Rozszerzenia: <umieść tu rozszerzenia, jedno w wierszu; każde odwołuje się do kroku scenariusza głównego> <zmieniony krok> <warunek>: <akcja albo podrzędny przypadek użycia> <zmieniony krok> <warunek>: <akcja albo podrzędny przypadek użycia> Lista wariantów technologii i danych: <umieść tu warianty, które ostatecznie mogą spowodować rozdzielenie scenariusza> <numer kroku lub wariantu> <lista wariantów> <numer kroku lub wariantu> <lista wariantów> Dodatkowa informacja: <cokolwiek jest potrzebne w przedsięwzięciu jako dodatkowa informacja>" Styl Rational Unified Process [5] http://localhost:8888/html/files/typy%20obiektowe.txt Strona 4 z 39 08.09.2013 09:00 1. Nazwa przypadku użycia 1.1. Krótki opis ... tekst ... 1.2. Aktorzy ... tekst ... 1.3. Wyzwalacze ... tekst ... 2. Przepływ zdarzeń 2.1. Przepływ podstawowy ... tekst ... 2.2. Przepływy alternatywne 2.2.1. Warunek 1 ... tekst ... 2.2.2. Warunek 2 ... tekst ... 2.2.3. ... 3. Specjalne wymagania 3.1. Platforma ... tekst ... 3.2. ... 4. Warunki początkowe ... tekst ... 5. Warunki końcowe ... tekst ... 6. Punkty rozszerzenia ... tekst ... Refaktoryzacja Brzydkie zapachy: 1. Powielony kod [1] - w dwóch metodach należących do tej samej klasy, - w dwóch podklasach tej samej klasy, - w dwóch niezwiązanych klasach. Możliwe rozwiązania: - wydzielenie metody, - wydzielenie metody i przemieszczenie metody w górę, - utworzenie metody szablonowej, - zastąpienie algorytmu, - wydzielenie klasy. Utworzenie metody szablonowej „Dwie metody w podklasach wykonują podobne, ale nie takie same kroki w tej samej kolejności. Umieść te kroki w metodach o takich samych sygnaturach po to by treść pierwotnych metod stała się taka sama. Następnie przemieść je do nadklasy.” abstract class Pracownik { public int wyliczPensje() { return wyliczPodstawe() + wyliczPremie(); } abstract protected int wyliczPodstawe(); abstract protected int wyliczPremie(); } class Księgowy extends Pracownik { protected int wyliczPodstawe() { return 5 * 40; } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 5 z 39 08.09.2013 09:00 protected int wyliczPremie() { return (int)(0.1 * 40); } } 2. Długa metoda [1] „kiedy tylko odczuwamy potrzebę umieszczenia w treści metody komentarza wyjaśniającego, tworzymy zamiast niego nową metodę. Taka metoda zawiera kod, który miał być (lub był) skomentowany, ale jej nazwa oddaje cel działania kodu, a nie sposób jego osiągnięcia.” Możliwe rozwiązania: - wydzielenie metody, - zastąpienie zmiennej tymczasowej przez zapytanie, - utworzenie klasy dla grupy parametrów, - przekazanie całego obiektu, - zastąpienie metody przez obiekt reprezentujący metodę. Zastąpienie zmiennej tymczasowej przez zapytanie „Pewnej zmiennej tymczasowej używa się do przechowywania wyniku obliczeń wartości jakiegoś wyrażenia. Wydziel obliczanie wartości wyrażenia do oddzielnej metody (której można teraz używać także w innych miejscach). Zastąp wszystkie referencje do zmiennej przez wywołania tej metody. class KontoBankowe { int wiek; boolean pelnoletni() { return wiek > 18; } int przelew() { if (pelnoletni()) { wykonajPrzelew(); } else { anuluj(); } } } Zastąpienie metody przez obiekt reprezentujący metodę „Użycie zmiennych lokalnych w pewnej metodzie uniemożliwia zastosowanie Wydzielenia metody. Zmień tę metodę w samodzielną klasę tak, by zmienne lokalne stały się polami tej klasy. Możesz wtedy dzielić metodę, tworząc nowe metody w tej klasie.” 3. Duża klasa [1] Możliwe rozwiązania: - wydzielenie klasy, - wydzielenie podklasy, - wydzielenie interfejsu. 4. Długa lista parametrów [1] Możliwe rozwiązania: - zastąpienie parametru przez metodę, - przekazanie całego obiektu, - utworzenie klasy dla grupy parametrów. Zastąpienie parametru przez metodę „Wynik wywołania jednej metody przekazuje się jako parametr wywołania następnej. Metoda pobierająca wartość parametru także może wywołać metodę, która te wartość dostarcza. Usuń parametr i pobieraj potrzebną wartość bezpośrednio wewnątrz metody, w której jest potrzebna.” 5. Rozbieżna zmiana [1] http://localhost:8888/html/files/typy%20obiektowe.txt Strona 6 z 39 08.09.2013 09:00 „Mówimy o rozbieżnej zmianie, jeśli pewna klasa jest często modyfikowana, na różne sposoby i z rozmaitych powodów. Jeżeli przyglądasz się klasie i mówisz: 'No cóż, trzeba będzie zmienić te trzy metody zawsze, gdy pojawi się nowa baza danych; a te cztery metody za każdym razem, gdy pojawi się nowy instrument finansowy', to jest to prawdopodobnie sytuacja, kiedy dwa obiekty byłyby lepsze niż jeden.” Możliwe rozwiązania: - wydzielenie klasy. 6. Poszatkowanie [1] „Poszatkowanie przypomina rozbieżną zmianę, ale jest jej przeciwieństwem. Stepuje zawsze wtedy, gdy wprowadzenie zmiany zmusza do wykonania wielu drobnych modyfikacji w różnych klasach.” Możliwe rozwiązania: - przemieszczenie metody, - przemieszczenie pola, - rozwinięcie klasy (usunięcie klasy po przemieszczeniu wszystkich jej elementów do innej). 7. Zazdrość o kod [1] „Podstawowym zadaniem obiektów jest grupowanie razem danych i operacji na tych danych. Często spotykanym brzydkim zapachem jest metoda, która wydaje się bardziej zainteresowana klasą inną niż własną. Najczęstszym przedmiotem takiej zazdrości są dane.” Możliwe rozwiązania: - przemieszczenie metody, - wydzielenie metody i przemieszczenie metody, 8. Zbitki danych [1] „Elementy danych są jak dzieci; lubią trzymać się razem. Często widuje się te same trzy lub cztery elementy danych w wielu miejscach: pola w kilku klasach, parametry w sygnaturach różnych metod. Zbitki danych powinno się umieścić w osobnym obiekcie.” Możliwe rozwiązania: - wydzielenie klasy, - utworzenie klasy dla grupy parametrów lub przekaż całego obiektu. 9. Obsesja typów podstawowych [1] „Początkujący programiści obiektowi niechętnie używają małych obiektów do wykonywania małych zadań, takich jak klasa reprezentująca pieniądze (lub parę składającą się z liczby i nazwy waluty), klasa reprezentująca zakres (czyli górna i dolna wartość) albo specjalnego rodzaju napisy, na przykład numery telefonu bądź kody pocztowe.” Możliwe rozwiązania: - zastąpienie prostych danych przez obiekt, - zastąpienie liczby oznaczającej typ przez klasę, - zastąpienie liczby oznaczającej typ przez podklasy, - zastąpienie liczby oznaczającej typ przez wzorzec Stan / Strategia, - wydzielenia klasy (dla pól), - utworzenia klasy dla grupy parametrów (dla parametrów), - zastąpienie tablicy przez obiekt (dla tablic). Zastąpienie prostych danych przez obiekt „Pewna porcja danych wymaga dodatkowego kodu lub kolejnych danych. Zastąp te dane przez obiekt.” Zastąpienie liczby oznaczającej typ przez klasę „Pewna klasa zawiera niewpływającą na jej działanie liczbę oznaczającą typ. Zastąp tę liczbę nową klasą.” http://localhost:8888/html/files/typy%20obiektowe.txt Strona 7 z 39 08.09.2013 09:00 class Osoba int int int int int } { grupa0 = 0; grupaA = 1; grupaB = 2; grupaAB = 3; grupaKrwi = grupa0; enum GrupaKrwi { GRUPA_0, GRUPA_A, GRUPA_B, GRUPA_AB } class Osoba { GrupaKrwi g = GrupaKrwi.GRUPA_0; } Zastąpienie liczby oznaczającej typ przez podklasy „Niezmienna wartość liczbowa oznaczająca typ wpływa na działanie pewnej klasy. Zastąp użycie takiej liczby przez podklasy.” class Socket { int UDP = 0; int TCP = 1; int type = UDP; public void send(){ if(type == TCP){ ... } else (type == UDP){ ... } } } abstract class Socket { abstract public void send(); } class TCPSocket extends Socket{ public void send(){ ... } } class UDPSocket extends Socket{ public void send(){ ... } } Zastąpienie liczby oznaczającej typ przez wzorzec Stan / Strategia „Pole przechowuje liczbę oznaczającą typ, której wartość ma wpływ na dzianie programu, ale nie można użyć dziedziczenia. Zastąp to pole przez obiekt oznaczający stan.” class Socket extends ABCD { int UDP = 0; int TCP = 1; int type = UDP; public void send(){ if(type == TCP){ ... http://localhost:8888/html/files/typy%20obiektowe.txt Strona 8 z 39 08.09.2013 09:00 } else (type == UDP){ ... } } } abstract class SocketImpl{ abstract void send(); } class UDPSocketImpl extends SocketImpl{ public void send(){... } } class TCPSocketImpl extends SocketImpl{ public void send(){...} } class Socket extends ABCD { private SocketImpl s; Socket(SocketImpl s){ this.s = s; } public void send(){ s.send(); } } Zastąpienie tablicy przez obiekt Na podstawie tablicy tworzona jest klasa. Następnie w kodzie tablica jest zastępowana przez obiekt tej klasy. String [] osoba = {"Jan", "Kowalski", "01.01.1951"}; Class Osoba { String imie; String nazwisko; Date dataUrodzenia; Osoba(String imie, String nazwisko, Data dataUrodzenia){ this.imie = imie; this.nazwisko = nazwisko; this.dataUrodzenia = dataUrodzenia; } } 10. Instrukcje switch [1] „Jedną z najbardziej widocznych cech kodu obiektowego jest brak w nim instrukcji switch (…) Jeśli występuje instrukcja switch, zwykle powinno się rozważyć użycie polimorfizmu.” Możliwe rozwiązania: - wydzielenie metody dla instrukcji switch i przemieszczenie metody, zastąpienie liczby oznaczającej typ przez podklasy lub zastąpienie liczby oznaczającej typ przez wzorzec Stan / Strategia, zastąpienie wyrażenia warunkowego przez polimorfizm, - zastąpienie użycia parametru przez wyspecjalizowane metody. Zastąpienie wyrażenia warunkowego przez polimorfizm „W wyrażeniu warunkowym wybrany kod zależy od typu określonego obiektu. Umieść kod każdej z gałęzi w przedefiniowanej metodzie odpowiedniej podklasy i zmień pierwotną http://localhost:8888/html/files/typy%20obiektowe.txt Strona 9 z 39 08.09.2013 09:00 metodę na abstrakcyjną.” class Kredyt{ int wyliczOplate(){ switch (type){ case HIPOTECZNY: return 0.01 * wartosc; case SAMOCHODOWY: return 100 + 0.05 * wartosc; case KONSUMPCYJNY: return 300; } } } abstract class Kredyt { abstract int wyliczOplate(); } class KredytHipoteczny { int wyliczOplate() { return 0.01 * wartosc; } } class KredytSamochodowy { int wyliczOplate() { return 100 + 0.05 * wartosc; } } class KredytKonsumpcyjny { int wyliczOplate() { return 300; } } „Jeżeli do rozważenia jest tylko kilka możliwych wartości i nie spodziewasz się, że pojawią się nowe to użycie polimorfizmu jest przesadą. ” Zastąpienie użycia parametru przez wyspecjalizowane metody Działanie pewnej metody całkowicie zależy od wartości przekazywanego parametru, traktowanego jak wartość typu wyliczeniowego. Utwórz oddzielna metodę dla każdej wartości parametru.” class Kredyt{ int wyliczOplateDlaKredytuHipotecznego(){ return 0.01 * wartosc; } int wyliczOplateDlaKredytuSamochodowego(){ return 100 + 0.05 * wartosc; } int wyliczOplateDlaKredytuKonsumpcyjnego(){ return 300; } } 11. Równoległe hierarchie dziedziczenia [1] „Równoległe hierarchie dziedziczenia stanowią szczególny przypadek poszatkowania. Polega to na tym, że za każdym razem kiedy tworzy się podklasę pewnej klasy,; trzeba też tworzyć podklasę pewnej innej klasy.” http://localhost:8888/html/files/typy%20obiektowe.txt Strona 10 z 39 08.09.2013 09:00 Możliwe rozwiązania: - przemieszczenie metody lub przemieszczenie pola (pod warunkiem, że obiekty z jednej hierarchii zawierają referencje do obiektów drugiej). 12. Leniwa klasa [1] „Pielęgnacja każdej klasy kosztuje. Klasę, która na siebie nie zarabia, powinno się usunąć.” Możliwe rozwiązania: - zwinięcie hierarchii (połączenie klasy z nadklasą), - rozwinięcie klasy (usunięcie klasy po przemieszczeniu wszystkich jej elementów do innej). 13. Spekulatywna ogólność [1] „Brian Foote wymyślił te nazwę dla zapachu, na który jesteśmy niezwykle wyczuleni. Czujesz go, kiedy ktoś mówi: 'Wydaje mi się, że kiedyś może to się przydać.' marzą mu się rozmaite triki i bajery potrzebne do obsługi przypadków, które w istocie nie są wymagane.” Możliwe rozwiązania: - zwinięcie hierarchii (klasy abstrakcyjne), - rozwinięcie klasy (niepotrzebne delegowanie), - usunięcie parametru (metody), - zmiana nazwy metody (metody). 14. Pole tymczasowe [1] „Zdarza się, że pewien obiekt zawiera zmienną egzemplarzową, której wartość jest ustawiana jedynie czasami. (…) Próba zrozumienia, dlaczego jakaś zmienna istnieje, skoro wydaje się nieużywana, może Cię doprowadzić do szału.” Możliwe rozwiązania: - „Użyj Wydzielenia klasy w celu utworzenia domu dla biednych osieroconych zmiennych. Umieść w nowej klasie cały kod, który korzysta z tych zmiennych ” 15. Łańcuchy wywołań [1] „Masz z nimi do czynienia, kiedy klient prosi jeden obiekt o referencje do innego obiektu. Po czym pyta ten drugi obiekt o następny, a ten z kolei o jeszcze inny.” Możliwe rozwiązania: - ukrycie delegowania (utworzenie metod ukrywających delegata), - wydzielenie metody i przemieszczenie metody. 16. Pośrednik [1] „Przyglądasz się interfejsowi jakiejś klasy i okazuje się, że połowa metod deleguje wywołania do jakiejś innej klasy.” Możliwe rozwiązania: - usunięcie pośrednika (zmiana kodu tak, by klient komunikował się bezpośrednio z delegatem), - rozwinięcia wywołań metody (zastąpieniu wywołania metody poprzez skopiowanie jej ciała w miejscu jej wywołania ), - zastąpienie delegowania przez dziedziczenie. 15. Zbytnia intymność [1] „Czasami klasy są za bardzo ze sobą związane i zbytnio interesują się prywatnością innych.” Możliwe rozwiązania: - przemieszczenie metody, http://localhost:8888/html/files/typy%20obiektowe.txt Strona 11 z 39 08.09.2013 09:00 - przemieszczenie pola, zmiana powiązania dwukierunkowego na jednokierunkowe, wydzielenie klasy, ukrycie delegowania, zastąpienie dziedziczenia przez delegowanie. 16. Podobne klasy o różnych interfejsach [1] Możliwe rozwiązania: - zmiana nazwy metody, - przemieszczanie metody, - wydzielenie nadklasy. 17. Niepełne klasy biblioteczne [1] Możliwe rozwiązania: - utworzenie metody obcej, - utworzenie lokalnego rozszerzenia. Utworzenie metody obcej „Pewnej klasy serwera, której kodu nie można niestety modyfikować przydałaby się nowa metoda. Utwórz w klasie klienta metodę, której pierwszym argumentem jest obiekt klasy serwera.” Utworzenie lokalnego rozszerzenia „W pewnej klasie serwera, której kodu nie można modyfikować, brakuje kilku przydatnych metod. Utwórz nową klasę zawierającą te nowe metody. Nowa klasa będzie opakowaniem lub podklasą klasy serwera.” 18. Odrzucony spadek [1] „Podklasy dziedziczą po swoich rodzicach metody i dane. Co się jednak dzieje, jeżeli ich nie chcą albo nie potrzebują.” Możliwe rozwiązania: - utworzenie nowej klasy oraz użycie przemieszczenia metody w dól i przemieszczenie metody w górę, - zastąpienie dziedziczenia przez delegowanie. 19. Komentarze [1] „To zadziwiające, jak często widzi się kod suto opatrzony komentarzami i zdaje sobie sprawę z tego, że są one tam dlatego, że kod jest źle napisany.” Możliwe rozwiązania: - wydzielenie metody, - zmiana nazwy metody, - dodanie asercji. Dodanie asercji Jeżeli komentarz opisuje pewne założenie na temat kodu, to zastosowanie asercji pozwoli jawnie zapisać to założenie. public class Socket { Server primary, secondary; Object read() { //the primary or the secondary server must be set if (primary != null) return primary.read(); else return secondary.read(); } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 12 z 39 08.09.2013 09:00 public static void main(String[] args) { Socket s = new Socket(); s.read(); } } public class Socket { Server primary, secondary; Object read() { assert (primary != null || secondary != null) : "the primary and the secondary server are ←֓ not set"; if (primary != null) return primary.read(); else return secondary.read(); } public static void main(String[] args) { Socket s = new Socket(); s.read(); } } 20. Klasy danych ”Klasy danych zawierają tylko pola, metody dostępu do nich i nic więcej. To bezmyślne pojemniki na dane i prawie na pewno inne klasy manipulują nimi w stopniu większym niż potrzeba.” Możliwe rozwiązania: • enkapsulacja pola dla pól publicznych, • enkapsulacja kolekcji dla kolekcji, • usunięcie metody ustawiającej, dla wszystkich pól, które powinny być tylko odczytywane, • przemieszczenie metody (patrz pkt. 2), • wydzielenie metody i przemieszczenie metody (patrz pkt. 2), • ukrycie metody dostępu. MCcabe Jedną z tradycyjnych miar produktów oprogramowania jest np. liczba wierszy kodu SLOC (ang. Source Lines of Code). Istnieje wiele wariantów dotyczących sposobu liczenia liczby wierszy kodu: - zliczanie fizycznych wierszy kodu – prosta metoda pozwalająca zliczyć wszystkie wiersze kodu, stosując ten wariant należy zdecydować w jaki sposób potraktować komentarze oraz puste linie: zliczać, nie zliczać lub może zliczać tylko niektóre, - zliczanie logicznych wierszy kodu – umożliwia zliczenie wykonywalnych wyrażeń znajdujących się w kodzie. Metryka: Złożoność cyklomatyczna McCabe’a CC = E − N + 2 ∗ P E – liczba krawędzi, N – liczba węzłów, P – liczba połączonych komponentów (węzłów wyjścia). CC Złożoność oraz ryzyko związane z testowaniem oraz utrzymaniem kodu 1–10 prosty kod bez dużego ryzyka http://localhost:8888/html/files/typy%20obiektowe.txt Strona 13 z 39 08.09.2013 09:00 11–20 bardziej złożony kod o średnim ryzyku 21-50 złożony kod o wysokim ryzyku 51+ niestabilny kod o bardzo wysokim ryzyku Metryki MIT zwane też metrykami C. K. Weighted Method per Class (WMC) „określająca metody ważone względem klasy, jest sumą złożoności statycznej klasy metod jednej klasy. Jeżeli przyjmie się, że złożoność każdej metody wynosi jeden, to metryka ta sprowadza się po prostu do liczby metod w klasie. ” Depth of Inheritance Tree (DIT) „określająca głębokość drzewa dziedziczenia” Number of Childer (NOC) „określająca liczbę potomków” (bezpośrednich) Coupling Between Objects (CBO) „określająca sprzężenie między obiektami, jest liczbą sprzężeń niezależnych od struktury klasyfikacyjnej. Nie rozróżnia asocjacji od relacji zespolenia czy używania – traktuje je wszystko jako wysyłanie komunikatów.” Dotyczy: typów atrybutów, parametrów, klauzul throws, typów zwracanych. Zazwyczaj podczas obliczania nie uwzględnia się: typów prymitywnych oraz klas z pakietu java.lang. Response for Class (RFC) „określająca reakcję na klasę, jest miarą strukturalnego sprzężenia klasy (. . . ). Zlicza ona metody dostępne w danej klasie albo bezpośrednio, albo za pośrednictwem komunikatu wysyłanego do drugiej klasy (...).” RFC = M + R M – liczba metod w klasie, R – liczba metod zdalnych (znajdujących się w innej klasie) wywołanych bezpośrednio przez metody z tej klasy. Lack of Cohesion of Methods (LCOM) „określająca niespójność metod (. . . ). Mierzy ona nienakładanie się zbiorów egzemplarzy zmiennych używanych przez metodę danej klasy. Istnieje jej alternatywna, operacyjne definicja: procent metod, które nie korzystają z atrybutów, osiągana średnio dla wszystkich atrybutów. Najniższa i najbardziej pożądana wartość metryki LCOM występuje wtedy, kiedy wszystkie metody używają wszystkich egzemplarzy zmiennych, największa zaś wtedy, kiedy żadnego egzemplarza zmiennej nie używa więcej niż jedna metoda.” Dla każdej pary metod w klasie: • jeżeli wykorzystują one rozłączne zbiory atrybutów to inkrementuj P, • jeżeli wykorzystują one co najmniej jeden wspólny atrybut to inkrementuj Q. if P > Q then LCOM = P − Q http://localhost:8888/html/files/typy%20obiektowe.txt Strona 14 z 39 08.09.2013 09:00 else LCOM = 0 end if Metryki MOOD - Attribute Hiding Factor (AHF), - Method Hiding Factori (MHF), - Attribute Inheritance Factor (AIF), - Method Inheritance Factor (MIF), - Polymorphism Factor (PF), - Coupling Factor (CF). Metryki R. Martina W metrykach R. Martina jako kategorię należy rozumieć grupę klas: moduł, pakiet, itp. Efferent Coupling (Ce) Liczba klas wewnątrz kategorii, która zależy od klas na zewnątrz tej kategorii. Afferent Coupling (Ca) Liczba klas na zewnątrz kategorii, która zależą od klas należących do tej kategorii. Instability (I) I = 0 oznacza maksymalnie stabilna kategorię, natomiast I = 1 oznacza maksymalnie niestabilną kategorię. Abstractness (A) Jest to stosunek abstrakcyjnych klas w kategorii do wszystkich klas w tej kategorii. WZORCE PROJEKTOWE Wzorce projektowe stanowią powtarzalne rozwiązanie zagadnień projektowych, z którymi się wciąż spotykamy Wzorce projektowe: Creational - dotyczą sposobu w jaki obiekty są tworzone. Obejmują wzorce: - Singleton (Singleton), - Pula Obiektów (Object Pool), - Prosta Fabryka (Simple Factory), - Metoda Wytwórcza (Factory Method), - Fabryka Abstrakcyjna (Abstract Factory), - Prototyp (Prototype), - Budowniczy (Builder), Structural - dotyczą projektowania obiektów, tak aby spełniały ograniczenia nałożone na projekt. Obejmują wzorce: - Adapter (Adapter), - Most (Bridge), - Kompozyt (Composite), - Fasada (Facade), - Dekorator (Decorator), - Pyłek (Flyweight), - Pośrednik (Proxy) http://localhost:8888/html/files/typy%20obiektowe.txt Strona 15 z 39 08.09.2013 09:00 Behavioral - dotyczą obiektów, które wykonują zadane akcje. Obejmują wzorce: - Odwiedzający (Visitor), - Iterator (Iterator), - Obserwator (Observer), - Stan (State), - Strategia (Strategy), - Metoda Szablonowa (Template Method), - Łańcuch Zobowiązań (Chain of Responsibility), - Interpreter (Interpreter), - Polecenie (Command), - Mediator (Mediator), - Pamiątka (Memento). Wzorzec Singleton jest stosowany w sytuacji, gdy dla danej klasy chcemy utworzyć tylko jeden obiekt. public class Singleton { private static Singleton s = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return s; } public void method(){} } public class TestSingleton { public static void main(String[] args) { Singleton s = Singleton.getInstance(); s.method(); }} Object Pool - Najczęstsze zastosowania: pula połączeń, pula wątków. Co 1. 2. 3. zrobić w sytuacji, gdy nie ma wolnych obiektów: Zgłosić informacje o błędzie do użytkownika (wyjątek, specjalna wartość). Dodać nowy obiekt do puli (zazwyczaj pule mają ograniczony rozmiar). Zablokować wątek aż obiekt będzie dostępny. final public class Rental<V> { private static class Entry<E> { boolean used; final E object; Entry(E object) { this.object = object; }} private int freePermits; private int usedPermits; final private Collection<Entry<V>> entries = new HashSet<Entry<V>>(); Rental(Collection<V> collection) { if (collection == null) http://localhost:8888/html/files/typy%20obiektowe.txt Strona 16 z 39 08.09.2013 09:00 throw new NullPointerException(); for (V e : collection) { if (e == null) throw new NullPointerException(); entries.add(new Entry<V>(e)); } freePermits = entries.size(); usedPermits = 0; } int getAvailable() { return freePermits; } public synchronized V rent() { if (freePermits > 0) { --freePermits; ++usedPermits; return getNext(); } else return null; } private V getNext() { for (Entry<V> e : entries) if (!e.used) { e.used = true; return e.object; } return null; } public synchronized void giveBack(V object) { if (tryReturn(object)) { ++freePermits; --usedPermits; }} private boolean tryReturn(V c) { for (Entry<V> e : entries) if (e.object == c) { if (!e.used) return false; e.used = false; return true; } return false; }} Simple Factory Polega na stworzeniu w klasie statycznej metody zwracającej obiekt jednej z możliwych klas zależnie od dostarczonych danych. Różnice w stosunku do użycia konstruktora: - można zwrócić obiekt klasy lub podklasy, - można zwrócić obiekt już istniejący (bez tworzenia nowego), - metodzie można nadać dowolną (opisową) nazwę. abstract public class Figure { abstract void draw(); public Figure getFigure(String name){ http://localhost:8888/html/files/typy%20obiektowe.txt Strona 17 z 39 08.09.2013 09:00 if(name.equals("circle")) return new Circle(); else if(name.equals("rectangle")) return new Rectangle(); return null; } class Circle extends Figure{ … } class Rectangle extends Figure{ … } } Factory Method Wzorzec ten definiuje interfejs do tworzenia obiektów, ale wybór do jakiej klasy będzie należał obiekt deleguje do podklasy. Inna nazwa: Wirtualny Konstruktor. interface NC {} //Network Connection class CableNC implements NC {} class WirelessNC implements NC {} abstract class NSP { //Network Service Provider protected abstract NC connect(); void doSomething(){ NC connection = connect(); //... } } class CableNSP extends NSP { protected NC connect() { return new CableNC(); } } class WirelessNSP extends NSP { protected NC connect() { return new WirelessNC(); } } class Client { public static void main(String arg[]) { NSP [] providers = {new CableNSP(), new WirelessNSP()}; for(NSP p : providers) p.doSomething(); } } Abstract Factory Dostarcza interfejsu do tworzenia rodzin związanych ze sobą albo zależnych obiektów bez podawania ich konkretnych klas. Abstrakcyjna fabryka może być traktowana jako fabryka fabryk. interface Frame {} class DefaultFrame implements Frame {} class MyFrame implements Frame {} http://localhost:8888/html/files/typy%20obiektowe.txt Strona 18 z 39 08.09.2013 09:00 interface Menu {} class DefaultMenu implements Menu {} class MyMenu implements Menu {} abstract class LAF { // LookAndFeel protected abstract Frame createFrame(); protected abstract Menu createMenu(); } class DefaultLAF extends LAF { @Override protected Frame createFrame() { return new DefaultFrame(); } @Override protected Menu createMenu() { return new DefaultMenu(); } } class MyLAF extends LAF { @Override protected Frame createFrame() { return new MyFrame(); } @Override protected Menu createMenu() { return new MyMenu(); } } class Runtime { static private LAF laf = new DefaultLAF(); static LAF setLAF(String name) { if (name.equals("MyLAF")) laf = new MyLAF(); else if (name.equals("DefaultLAF")) laf = new DefaultLAF(); return laf; } } class Client { public static void main(String arg[]) { LAF laf = Runtime.setLAF("MyLAF"); Frame f = laf.createFrame(); Menu m = laf.createMenu(); } } Prototype Umożliwia tworzenie obiektów na podstawie prototypowej instancji poprzez kopiowanie prototypu. class Prototype implements Cloneable{ static private Prototype instance = new Prototype(); class Prototype implements Cloneable{ static private Prototype instance = new Prototype(); private Prototype(){} public static Prototype getPrototype(){ return instance; http://localhost:8888/html/files/typy%20obiektowe.txt … Strona 19 z 39 08.09.2013 09:00 } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } private int i = -1; private Field f = new Field(); public void setI(int i) { this.i = i; } @Override public String toString(){ return super.toString() + " " + i + " " + f.toString(); } } class Field{} public class Test { public static void main(String arg[]) throws CloneNotSupportedException { Prototype p = Prototype.getPrototype(); p.setI(1); Prototype p2 = (Prototype) p.clone(); p.setI(2); System.out.println(p); System.out.println(p2); } } Builder Wzorzec ten pozwala na rozdzielenie konstrukcji złożonego obiektu od jego reprezentacji. class FrameBuilder { private int x, y; private int width, height; private String title; public void setPosition(int x, int y) { this.x = x; this.y = y; } public void setSize(int width, int height) { this.width = width; this.height = height; } public void setTitle(String title) { this.title = title; } public Frame build() { if (x != 0 && y != 0 && width != 0 && height != 0 && title != null) { JFrame f = new JFrame(); f.setSize(width, height); f.setTitle(title); http://localhost:8888/html/files/typy%20obiektowe.txt Strona 20 z 39 08.09.2013 09:00 f.setLocation(x, y); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); return f; } return null; } } public class TestBuilder { public static void main(String[] args) { FrameBuilder b = new FrameBuilder(); b.setSize(640, 480); b.setPosition(100, 100); b.setTitle("Test frame"); Frame f = b.build(); } } Adapter Wzorzec ten ma za zadanie przekształcenie jednego interfejsu w drugi. class EnumerationAdapter<E> implements Enumeration<E> { private Iterator<E> i; EnumerationAdapter(Iterator<E> i) { this.i = i; } @Override public boolean hasMoreElements() { return i.hasNext(); } @Override public E nextElement() { return i.next(); } } public class Adapter { public static void main(String[] args) { HashSet<Integer> s = new HashSet<Integer>(Arrays.asList(1,2,3)); // testEnumeration(s.iterator()); testEnumeration(new EnumerationAdapter<Integer>(s.iterator())); } public static void testEnumeration(Enumeration<?> e) { while (e.hasMoreElements()) System.out.println(e.nextElement()); } } Bridge Wzorzec mający na celu oddzielenie interfejsu od implementacji obiektu. interface Storable { public void add(int id, Object o); public void update(int id, Object o); http://localhost:8888/html/files/typy%20obiektowe.txt Strona 21 z 39 08.09.2013 09:00 public Object get(int id); public void delete(int id); } class Storage implements Storable { private StorageMethod s; public Storage(StorageMethod s) { this.s = s; } @Override public void add(int id, Object o) { s.put(id, o); } @Override public void update(int id, Object o) { s.put(id, o); } @Override public Object get(int id) { return s.get(id, false); } @Override public void delete(int id) { s.get(id, true); } } interface StorageMethod { public void put(int id, Object o); public Object get(int id, boolean delete); } class ArrayStorageMethod implements StorageMethod { private Object[] a = new Object[1024]; public void put(int id, Object o) { a[id] = o; } public Object get(int id, boolean delete) { Object tmp = a[id]; if (delete) a[id] = null; return tmp; } } class MapStorageMethod implements StorageMethod { private HashMap<Integer, Object> m = new HashMap<Integer, Object>(); public void put(int id, Object o) { m.put(id, o); } public Object get(int id, boolean delete) { if (delete) return m.remove(id); http://localhost:8888/html/files/typy%20obiektowe.txt Strona 22 z 39 08.09.2013 09:00 else return m.get(id); } } public class TestBridge { public static void main(String[] args) { Storage s = new Storage(new ArrayStorageMethod()); s.add(1, "TEST#1"); System.out.println(s.get(1)); s = new Storage(new MapStorageMethod()); s.add(2, "TEST#2"); System.out.println(s.get(2)); } } Composite Umożliwia reprezentowanie złożonych struktur drzewiastych reprezentujących hierarchie całość – część. Pozwala traktować zarówno pojedyncze obiekty jak i całe ich struktury w taki sam sposób. interface Element { void list(String space); } class File implements Element { String name; File(String name) { this.name = name; } public void list(String space) { System.out.println(space + "File: " + name); } } class Dir extends ArrayList<Element> implements Element { String name; public Dir(String name) { this.name = name; } public void list(String space) { System.out.println(space + "Dir: " + name); Iterator<Element> i = iterator(); while (i.hasNext()) i.next().list(space + " "); } } public class TestComposite { public static void main(String[] args) { Dir c1 = new Dir("C:"); c1.add(new File("autoexec.bat")); c1.add(new File("boot.ini")); Dir c2 = new Dir("Windows"); http://localhost:8888/html/files/typy%20obiektowe.txt Strona 23 z 39 08.09.2013 09:00 c2.add(new File("notepad.exe")); c1.add(c2); c1.list(""); } } Facade Wzorzec służy do zapewnia uproszczonego interfejs do skomplikowanego podsystemu. Przykład: dostęp do bazy danych. class Database { private private private private private Connection connection; OracleDataSource dataSource; CallableStatement proc; Statement statement; ResultSet resultSet; public Database(URL url) { connection = DriverManager.getConnection(url); } public static void execProcedure(String procedureName) throws SQLException { try { proc = connection.prepareCall("BEGIN " + procedureName + "; END;"); proc.execute(); } finally { if (proc != null) proc.close(); } } … } public class TestFacade { public static void main(String[] args) throws MalformedURLException { Database d = new Database( new URL("jdbc:oracle:thin:user/password@host:port:database")); d.execProcedure("test()") … } } Decorator Wzorzec pozwala na zmianę właściwości poszczególnych obiektów bez potrzeby tworzenia nowej klasy pochodnej. interface Output{ void writeln(String s); } class StandardOutput implements Output { public void writeln(String s){ System.out.println(s); } } abstract class Decorator implements Output{ http://localhost:8888/html/files/typy%20obiektowe.txt Strona 24 z 39 08.09.2013 09:00 protected Output output; Decorator(Output output){ this.output = output; } } class UpperOutput extends Decorator{ UpperOutput(Output output) { super(output); } @Override public void writeln(String s) { output.writeln(s.toUpperCase()); } } class ReverseOutput extends Decorator{ ReverseOutput(Output output) { super(output); } @Override public void writeln(String s) { output.writeln(new StringBuffer(s).reverse().toString()); } } public class TestDecorator { public static void main(String[] args) { Output output = new StandardOutput(); output.writeln("Ala ma kota Filemona"); output = new StandardOutput(); output = new UpperOutput(output); output.writeln("Ala ma kota Filemona"); output = new StandardOutput(); output = new ReverseOutput(output); output.writeln("Ala ma kota Filemona"); output = new StandardOutput(); output = new UpperOutput(output); output = new ReverseOutput(output); output.writeln("Ala ma kota Filemona"); } } Flyweight Wzorzec pozwala zredukować liczbę małych obiektów reprezentujących dane. Dwie metody przechowywania znaków dokumencie: 1. Dla każdego znaku w dokumencie przechowujemy jeden obiekt reprezentujący jego symbol graficzny. 2. Dla każdego rodzaju znaku w dokumencie przechowujemy jeden obiekt reprezentujący jego symbol graficzny oraz dodatkowo jego pozycje. public class TestFlyweight { public static void main(String[] args) { String s1 = new String("TEST"); String s2 = new String("TEST"); System.out.println((s1 == s2)); String s3 = "TEST"; String s4 = "TEST"; http://localhost:8888/html/files/typy%20obiektowe.txt Strona 25 z 39 08.09.2013 09:00 System.out.println((s3 == s4)); } } Proxy Wzorzec ten jest wykorzystywany do reprezentowania jednego obiektu za pomocą drugiego. Najczęstsze powody wykorzystywania tego wzorca: - opóźnienie utworzenia obiektu, - dostęp do obiektu znajdującego się na innej maszynie, - ograniczenie praw dostępu do obiektu, - konieczność wykonywania dodatkowych akcji podczas dostępu do obiektu. Visitor Wzorzec ten pozwala na dodawanie nowych operacji bez wprowadzania zmian do klas, na których operuje. interface GraphicComponent { void accept(Visitor v); } interface Visitor { void visit(Label b); void visit(Button b); void visit(Window b); } class Label implements GraphicComponent { @Override public void accept(Visitor v) { v.visit(this); } public int getWidth() { return 100; } } class Button implements GraphicComponent { @Override public void accept(Visitor v) { v.visit(this); } public int getBegin() { return 0; } public int getEnd() { return 200; } } class SizeVisitor implements Visitor { @Override public void visit(Label b) { System.out.println("Size = " + b.getWidth()); } @Override public void visit(Button b) { System.out.println("Size = " + (b.getEnd() - b.getBegin())); } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 26 z 39 08.09.2013 09:00 @Override public void visit(Window b) { System.out.println("Size = " + "???"); } } class NameVisitor implements Visitor { @Override public void visit(Label b) { System.out.println("Name = " + b.getClass().getName()); } @Override public void visit(Button b) { System.out.println("Name = " + b.getClass().getName()); } @Override public void visit(Window b) { System.out.println("Name = " + b.getClass().getName()); } } class Window implements GraphicComponent { GraphicComponent[] g = { new Button(), new Label() }; @Override public void accept(Visitor v) { for (GraphicComponent elem : g) { elem.accept(v); } v.visit(this); } } public class TestVisitor { public static void main(String[] args) { Window w = new Window(); w.accept(new SizeVisitor()); w.accept(new NameVisitor()); } } Iterator Pozwala na sekwencyjny dostęp do obiektów znajdujących się w kolekcji bez ujawniania jej szczegółów. class IteratorImpl<E> implements Iterator<E>{ private E t[]; private int counter; IteratorImpl(E t[]){ this.t = t; counter = 0; } public boolean hasNext() { http://localhost:8888/html/files/typy%20obiektowe.txt Strona 27 z 39 08.09.2013 09:00 return counter < t.length; } public E next() { return t[counter++]; } public void remove() { t[counter] = null; } } class CollectionImpl<E> implements Collection<E>{ private E t[]; public CollectionImpl(E t[]){ this.t = t; } public boolean add(E e) { throw new RuntimeException(); } public boolean addAll(Collection<? extends E> c) { throw new RuntimeException(); } public void clear() { throw new RuntimeException(); } public boolean contains(Object o) { throw new RuntimeException(); } public boolean containsAll(Collection<?> c) { throw new RuntimeException(); } public boolean isEmpty() { throw new RuntimeException(); } public Iterator<E> iterator() { return new IteratorImpl<E>(t); } public boolean remove(Object o) { throw new RuntimeException(); } public boolean removeAll(Collection<?> c) { throw new RuntimeException(); } public boolean retainAll(Collection<?> c) { throw new RuntimeException(); } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 28 z 39 08.09.2013 09:00 public int size() { throw new RuntimeException(); } public Object[] toArray() { throw new RuntimeException(); } public <T> T[] toArray(T[] a) { throw new RuntimeException(); } } public class TestIterator { public static void main(String[] args) { Integer t [] = {1,2,3}; CollectionImpl<Integer> c = new CollectionImpl<Integer>(t); Iterator<Integer> i = c.iterator(); while(i.hasNext()) System.out.println(i.next()); } } Observer Wzorzec ten jest wykorzystywany w sytuacji, gdy wybrane obiekty chcą być informowane o zmianach stanu określonego obiektu. interface Observer { void update(Observable o, Object arg); } class Observable { private Observer o; public void addObserver(Observer o) { this.o = o; } public void notifyObservers(Object object) { o.update(this, object); } } public class TestObserver { public static void main(String[] args) { Observable observable = new Observable(); observable.addObserver(new Observer(){ public void update(Observable o, Object arg) { System.out.println("update " + arg); }}); observable.notifyObservers("!!!"); } } State Wzorzec ten służy do zmiany zachowania obiektu gdy zmianie ulegnie jego wewnętrzny stan. Na zewnątrz obiekt ten zachowuje się tak jakby należał do innej klasy. http://localhost:8888/html/files/typy%20obiektowe.txt Strona 29 z 39 08.09.2013 09:00 interface State{ public void click(); } class Active implements State{ public void click() { System.out.println("Action"); } } class NotActive implements State{ public void click() { System.out.println("No action"); } } class Button{ private State s; public Button(boolean active) { setActive(active); } public void setActive(boolean active){ if(active) s = new Active(); else s = new NotActive(); } public void click(){ s.click(); } } public class TestState { public static void main(String[] args) { Button c = new Button(true); c.click(); c.setActive(false); c.click(); c.setActive(true); c.click(); } } Strategy Wzorzec służy do definiowania grupy algorytmów, które mogą być stosowane wymiennie. Posiadają one spólny interfejs a różnią się implementacją. interface Sorter<E> { public Collection<E> sort(Collection<E> e); } class QuickSort<E> implements Sorter<E> { public Collection<E> sort(Collection<E> e) { // ... } } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 30 z 39 08.09.2013 09:00 class BubbleSort<E> implements Sorter<E> { public Collection<E> sort(Collection<E> e) { // ... } } public class Context<E> implements Sorter<E> { private Sorter<E> s; Context(Sorter<E> s) { this.s = s; } public void changeSorter(Sorter<E> s) { this.s = s; } public Collection<E> sort(Collection<E> e) { return s.sort(e); } } Template Method Wzorzec ten stosowany jest do definiowania szkieletu algorytmu pozostawiając jednocześnie zdefiniowanie wybranych jego elementów w podklasach. abstract class Sorter { int[] data; Sorter(int i) { data = new int[i]; } void execute() { readData(); sort(); writeData(); } void readData() { System.out.println("Reading data"); } abstract protected void sort(); void writeData() { System.out.println("Writing data"); } } class QucickSort extends Sorter { QucickSort(int i) { super(i); } protected void sort() { System.out.println("Sorting data"); } } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 31 z 39 08.09.2013 09:00 public class TestTemplateMethod { public static void main(String[] args) { QucickSort s = new QucickSort(10); s.execute(); } } Chain of Responsibility Rozwala na luźne powiązanie pomiędzy nadawcą oraz odbiorcą, pozwala na przetwarzanie żądania przez więcej niż jednego odbiorcę. Żądanie przekazywane jest przez łańcuch odbiorców aż do momentu obsłużenia. class Request { private int i; Request(int i){ this.i = i; } int getI(){ return i; } } abstract class Handler { protected Handler nextHandler; public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } public void handleRequest(Request request) { if (!handleRequestLoally(request)) { if (nextHandler != null) nextHandler.handleRequest(request); else throw new RuntimeException(); } } abstract protected boolean handleRequestLoally(Request request); } class MyHandler extends Handler { private int divider; MyHandler(int divider) { this.divider = divider; } @Override protected boolean handleRequestLoally(Request request) { if (request.getI() % divider == 0) { System.err.println("Request (" + request.getI() + ") handled (" + divider + ")"); return true; } else { System.err.println("Request (" + request.getI() + ") not handled (" + divider + ")"); return false; } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 32 z 39 08.09.2013 09:00 } } public class Test { public static void main(String[] args) { Handler h2 = new MyHandler(2); Handler h3 = new MyHandler(3); Handler h5 = new MyHandler(5); h2.setNextHandler(h3); h3.setNextHandler(h5); Request[] requests = { new Request(4), new Request(9), new Request(25), new Request(1) }; for (Request r : requests) h2.handleRequest(r); } } Interpreter - służy do tworzenie interpretera języka o określonej gramatyce. public class Context { private String input; private int output; public Context(String input) { this.input = input; } public String getInput() { return input; } public void setInput(String input) { this.input = input; } public int getOutput() { return output; } public void setOutput(int output) { this.output = output; } } public abstract class Expression { public void interpret(Context context) { if (context.getInput().length() == 0) return; if (context.getInput().startsWith(nine())) { context.setOutput(context.getOutput() + (9 * multiplier())); context.setInput(context.getInput().substring(2)); } else if (context.getInput().startsWith(four())) { context.setOutput(context.getOutput() + (4 * multiplier())); context.setInput(context.getInput().substring(2)); } else if (context.getInput().startsWith(five())) { context.setOutput(context.getOutput() + (5 * multiplier())); context.setInput( context.getInput().substring(1)); } while (context.getInput().startsWith(one())) { http://localhost:8888/html/files/typy%20obiektowe.txt Strona 33 z 39 08.09.2013 09:00 context.setOutput(context.getOutput() + (1 * multiplier())); context.setInput(context.getInput().substring(1)); } } public public public public public } abstract abstract abstract abstract abstract String one(); String four(); String five(); String nine(); int multiplier(); public public public public public public } class ThousandExpression extends Expression{ String one() { return "M"; } String four(){ return " "; } String five(){ return " "; } String nine(){ return " "; } int multiplier() { return 1000; } public public public public public public } class HundredExpression extends Expression{ String one() { return "C"; } String four(){ return "CD"; } String five(){ return "D"; } String nine(){ return "CM"; } int multiplier() { return 100; } public public public public public public } class TenExpression extends Expression{ String one() { return "X"; } String four(){ return "XL"; } String five(){ return "L"; } String nine(){ return "XC"; } int multiplier() { return 10; } public public public public public public } class OneExpression extends Expression{ String one() { return "I"; } String four(){ return "IV"; } String five(){ return "V"; } String nine(){ return "IX"; } int multiplier() { return 1; } public class MainInterpreter { /** * @param args */ public static void main(String[] args) { String roman = "MCMXXVIII"; Context context = new Context(roman); // Build the 'parse tree' ArrayList<Expression> tree = new ArrayList<Expression>(); tree.add(new ThousandExpression()); tree.add(new HundredExpression()); tree.add(new TenExpression()); tree.add(new OneExpression()); // Interpret for (Iterator it = tree.iterator(); it.hasNext();) http://localhost:8888/html/files/typy%20obiektowe.txt Strona 34 z 39 08.09.2013 09:00 { Expression exp = (Expression)it.next(); exp.interpret(context); } System.out.println(roman + " = " + Integer.toString(context.getOutput())); } } Command - umożliwia traktowanie żądania jako obiektu, dzięki czemu możliwe jest ich kolejkowanie, logowanie, wycofywanie, itp. interface Executable { void execute(); } class OneThreadExecutor { private LinkedBlockingQueue<Executable> q; OneThreadExecutor() { q = new LinkedBlockingQueue<Executable>(); new Thread() { @Override public void run() { try { while (true) q.take().execute(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } public void cancel(Executable task) { q.remove(task); } public void execute(Executable task) { q.add(task); } } class MyExecutable implements Executable{ private int time; private String text; MyExecutable(int time, String text){ this.time = time; this.text = text; } @Override public void execute() { try { TimeUnit.SECONDS.sleep(time); } catch (InterruptedException e) { http://localhost:8888/html/files/typy%20obiektowe.txt Strona 35 z 39 08.09.2013 09:00 e.printStackTrace(); } System.out.println(text); } } public class TestCommand { public static void main(String[] args) { OneThreadExecutor e = new OneThreadExecutor(); MyExecutable longTask = new MyExecutable(3, "Long task"); MyExecutable shortTask = new MyExecutable(1, "Short task"); MyExecutable veryLongTask = new MyExecutable(5, "Very long task"); e.execute(longTask); e.execute(shortTask); e.execute(veryLongTask); e.cancel(shortTask); } } Mediator Definiuje obiekt, który ukrywa sposób interakcji pomiędzy grupą obiektów. Umożliwia tworzenie luźnych powiązań pomiędzy obiektami poprzez umożliwienie usunięcia przechowywania referencji bezpośrednio. class Person { private String firstName; private String lastName; protected Chat chat; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public void register(Chat chat) { this.chat = chat; } @Override public String toString() { return firstName + " " + lastName; } } class Student extends Person { private String group; public Student(String firstName, String lastName, String group) { super(firstName, lastName); this.group = group; } public String getGroup() { return group; } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 36 z 39 08.09.2013 09:00 public void send(String text) { chat.send(this, text); } } class Teacher extends Person { private String specialization; public Teacher(String firstName, String lastName, String specialization){ super(firstName, lastName); this.specialization = specialization; } public String getSpecialization() { return specialization; } public void send(String text) { chat.send(this, text); } } class Chat { private HashSet<Teacher> teacher = new HashSet<Teacher>(); private HashMap<String, HashSet<Student>> students = new HashMap<String, HashSet<Student>>(); public void register(Student s) { HashSet<Student> group = students.get(s.getGroup()); if (group == null) { group = new HashSet<Student>(); students.put(s.getGroup(), group); } group.add(s); s.register(this); } public void register(Teacher t) { teacher.add(t); t.register(this); } public void send(Student s, String m) { HashSet<Student> group = students.get(s.getGroup()); for (Student tmp : group) { if (s != tmp) System.out.println( "Student " + s + " sends " + m + " to " + tmp); } } public void send(Teacher t, String m) { for (HashSet<Student> group : students.values()) for (Student tmp : group) { System.out.println( "Teacher "+ t + " sends " + m + " to " + tmp); } } } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 37 z 39 08.09.2013 09:00 public class TestChatRoom { public static void main(String[] args) { Chat c = new Chat(); Student sA = new Student("AAA", "AAA", "1"); c.register(sA); Student sB = new Student("BBB", "BBB", "1"); c.register(sB); Student sC = new Student("CCC", "CCC", "1"); c.register(sC); Student sX = new Student("XXX", "XXX", "2"); c.register(sX); Student sY = new Student("YYY", "YYY", "2"); c.register(sY); Student sZ = new Student("ZZZ", "ZZZ", "2"); c.register(sZ); Teacher t = new Teacher("TTT", "TTT", "Computer science"); c.register(t); sA.send("Hello"); sX.send("Hello"); t.send("Hello"); } } Memento Wzorzec ten, nie naruszając hermetyzacji, pozwala na zapis stanu obiektu tak aby obiekt mógł powrócić do tego stanu. Pozwala to na realizację „punktów kontrolnych” oraz przywrócenia poprzedniego stanu obiektu. Wzorzec ten definiuje trzy podstawowe role [1]: - Originator (Sprawca) – obiekt, którego stan chcemy zapisywać, - Memento – obiekt, który zapisuje stan, - Caretaker (Dozorca) – zarządza zapisem stanu, zapisuje Memento i używa go, gdy jest potrzebny do przywrócenia stanu. Memento powinien posiadać dwa interfejsy: - używany przez Caretaker, pozwala tylko na przekazanie obiektu Memento do innego obiektu, - używany przez Originator, pozwalający przywrócić poprzedni stan obiektu, class Originator { private int state; public void incState() { state++; } public Memento createMemento() { return new Memento(state); } public void setMemento(Memento m) { this.state = m.state; } @Override public String toString(){ return super.toString() + " " + state; } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 38 z 39 08.09.2013 09:00 static class Memento { private int state; Memento(int state) { this.state = state; } } } class Caretaker { private Originator.Memento memento; public Originator.Memento getMemento() { return memento; } public void setMemento(Originator.Memento memento) { this.memento = memento; } } public class TestMemento { public static void main(String[] args) { Originator o = new Originator(); o.incState(); System.out.println(o); Caretaker c = new Caretaker(); Originator.Memento oldState = o.createMemento(); c.setMemento(oldState); o.incState(); System.out.println(o); o.setMemento(c.getMemento()); System.out.println(o); } } http://localhost:8888/html/files/typy%20obiektowe.txt Strona 39 z 39