tp.docx

(563 KB) Pobierz

GRASP: Creator, Expert, Low Coupling, High Cohesion, Controller;

GoF – wzorce czynnościowe- zachowanie, współpraca obiektów:

Strategy, Observer, State, Command, Template Method

GoF – wzorce kreacyjne – tworzenie, inicjalizacja I konfiguracja obiektów:

Singleton, Simple Factory, Factory Method, Abstract Factory, Builder;

GoF – w. strukturalne – struktury powiązań obiektów:

Adapter, Composite, Facade, Proxy, Decorator;

GRASP-Polega (głównie) na przydziale metod do klas. Creator - instancje A powinna tworzyć klasa B, która spełnia poniższe warunki (im więcej tym lepiej):; -B żzawieraż, zapamiętuje lub bezpośrednio używa A; -B posiada dane inicjalizacyjne dla A; -gdy wiele obiektów spełnia warunki, wybierz B żzawierająceż A; Zysk: mniejsza liczba powiązań, kod łatwy w utrzymaniu Uwagi:; -czasem lepiej oddelegować obowiązek tworzenia obiektów do klasy pomocniczej (będzie przy Abstract Factory); -operacje createSystem() projektuje się na samym końcu; Expert - czyli ten, który wie, robi Problem: jak przydzielać obiektom zobowiązania? Rozwiązanie: przydziel zobowiązanie żekspertowiż - czyli klasie, która posiada informacje konieczne do jego realizacji(zasada animacji obiektów) Zysk: wspiera hermetyzację, Low Coupling i High Cohesion; równomierny podział odpowiedzialności - żlżejsze żklasy Uwagi: informacja może być rozproszona po rożnych obiektach (konieczna współpraca ekspertów); czasem może być w sprzeczności z High Cohesion (np. dostęp do bazy danych). Coupling (Sprzężenie) - Stopień sprzężenia-Miara określająca w ja k im stopniu dany klasa je s t zależna od innych obiektów (klas, bibliotek, narzędzi). Typowe zależności: atrybut, argument metody, wywołanie Metod zewnętrznej, dziedziczenie, implementacja interfejsu. Problemy związane z wysokim sprzężeniem klasy A: zmiany w powiązanych klasach wymuszają zmiany w A; trudno jest żzrozumiećż klasę A w izolacji; trudno jest powtórnie użyć klasę A, bo potrzebne są też obiekty z nią powiązane; Low Coupling: Problem: Jak zmniejszyć liczbę zależności i zasięg zmian, a zwiększyć możliwości ponownego wykorzystania kodu? Rozwiązanie: Przydziel odpowiedzialności ta k by ograniczyć stopień sprzężenia. Stosuj tę zasadę podczas oceny rożnych możliwości projektowych (zasada ewaluacyjna). Zysk: łatwiejsze rozumienie kodu i jego ponowne wykorzystanie, ograniczenie zasięgu zmian. Uwagi: sprzęganie klas ze stabilnymi, wszechobecnymi i łatwo dostępnymi elementami nie jest problemem (np. java.util).; Pewien stopień powiązania jest nieunikniony, bo obiekty muszą się komunikować. Cohesion (Spójność): Stopień spójności- Miara określająca w ja k im stopniu zobowiązania (metody) danej klasy są do siebie podobne i ze sobą powiązane. Klasa o niskiej spójności wykonuje niepowiązane ze sobą zadania lub wykonuje zbyt dużo zadań. Powoduje to następujące problemy: trudno zrozumieć kod i cel takiej klasy; trudno ją ponownie wykorzystać; trudno ją utrzymywać i rozwijać; klasa jest żdelikatna ż i podatne na wpływ zmian; Bardzo niska spójność - klasa odpowiada za wiele zadań, w rożnych obszarach funkcjonalnych np. klasa opowiada za logikę i za interakcję z bazą danych); Niska spójność - klasa w pełni odpowiada za złożone zadanie w jednym obszarze funkcjonalnym (np. jedna klasa w pełni odpowiada

Za interakcję z bazą danych); Wysoka spójność - klasy o umiarkowanej liczbie zobowiązań w jednym obszarze funkcjonalnym i współpracujące w celu ich realizacji np. grupa klas realizujących dostęp do bazy danych); Umiarkowana spójność - klasa jest w pełni odpowiedzialna za wykonanie niewielkich powiązanych z konceptem klasy, ale nie ze sobą nawzajem (np. klasa Company odpowiadająca za znajomość pracowników i dane finansowe); High Cohesion: Problem: Jak sprawić by obiekty miały jasny cel, były łatwe w utrzymaniu i zrozumiałe? Rozwiązanie: Przydziel odpowiedzialności tak, by spójność została wysoka. Stosuj tę zasadę podczas oceny rożnych Możliwości projektowych (zasada ewaluacyjna). Zysk: przejrzysty projekt, łatwe powtórne wykorzystanie kodu; High Cohesion <->Low Coupling (np. klasa odpowiedzialna za GUI, logikę biznesową i Kontakt z bazą danych jest nie tylko wysoce niespójna, ale też musi wchodzić w interakcje z wieloma obiektami); Controller: Problem:, który z obiektów poza warstwą Ul pierwszy odbiera żądania operacji systemowych i deleguje ich wykonanie? Rozwiązanie: przydziel tą odpowiedzialność do klasy X spełniającej jeden z następujących warunków: klasa X reprezentuje cały system ( kontroler fasadowy); klasa X reprezentuje jeden scenariusz przypadku użycia, w ramach, którego wykonywana jest operacja systemowa ( kontroler sesyjny, typowo: <UseCaseName>Session) Zyski: logika aplikacji niezależna od interfejsu, wiele interfejsów; kontroler fasadowy, wygodny, gdy mało zdarzeń systemowych; kontroler sesyjny umożliwia kontrolę stanu przypadku użycia; Architektura warstwowa(MVC): Warstwa to grupa klas, pakietów, lub podsystemów, który mają wspólny zakres odpowiedzialności, związany z ważnym aspektem systemu; Warstwy układa się tak, aby wyższe warstwy wywoływały usługi niższych ( najlepiej sąsiednich warstw); W systemach obiektowych najczęściej wyróżnia się warstwy: Interfejs użytkownika (Ul lub GUI); Logika aplikacji i obiekty dziedzinowe; Usługi techniczne np. połączenie z bazą danych, logi); Polymorphism: - w naszym kontekście oznacza nadanie tej samej nazwy metodom różnych klas, gdy metody te są związane ze sobą (poszczególne klasy często mają wspólny interfejs lub nadklasę); Sposób wiązanie wywołań z metodami zależy od języka: statyczne - w trakcie kompilacji; dynamiczne - w czasie wykonania programu.; Metody przeciążone (overloaded) są wiązane statyczne, metody przesłonięte (overriden) dynamicznie, (po co jest adnotacja @Override ?) ;Wiązanie statyczne jest oparte na typach, wiązanie dynamiczne na obiektach; Metody final, static, private - wiązanie statyczne (nie można ich przesłonić); Metody virtual wiązanie dynamiczne; O ile nie istnieje domyślna czynność, operacja polimorficzna w nadklasie powinna zostać zadeklarowana, jako abstrakcyjna. Kiedy stosować interfejsy? Gdy chcemy uzyskać polimorfizm niezależnie od hierarchii klas: dziedziczenie jednokrotne w Java czy C# to silne ograniczenie.; Czasem tworzy się interfejs na wszelki wypadek (większa elastyczność), warto jednak krytycznie rozważyć szanse pojawienia się nowych typów obiektów w przyszłości; Pure Fabrication – przykład: Chcemy zapisywać niektóre obiekty w bazie danych ( np. Sale); Jeśli każdej klasie przydzielimy odpowiedzialność za jej zapis do bazy, zwiększymy sprzężenie i zmniejszymy spójność, ponadto podobny kod będzie się pojawiał w wielu miejscach; Piszemy klasę PersistentStorage nie mającą odpowiednika w modelu dziedziny, która odpowiada tylko za zapis do bazy (może to być fasada, za którą jest wiele klas pomocniczych); Celem użycia Pure Fabrication jest grupowanie powiązanych ze sobą zachowań ( dekompozycja behawioralna, a nie obiektowa); Indirection - czyli pośrednictwo: Rozwiązanie: Przydziel zobowiązania obiektowi, który będzie pośredniczył między innymi obiektami, tak by nie były one bezpośrednio związane;Protected Variations, czyli ochrona przed zmiennością-Rozwiązanie: Rozpoznaj miejsca, w których mogą pojawić się zmiany i przydziel odpowiedzialność tak, by otoczyć te miejsca stabilnym interfejsem (uwaga: interfejs ma tutaj szersze znaczenie niż w Java!); Programowanie sterowane danymi: Odczytywanie danych [kodu, zmiennych, ścieżek, nazw klas...) z zewnętrznych źródeł w celu parametryzacji systemu; Ochrona przed zmiennością poprzez wyprowadzanie zmiennego elementu na zewnątrz, odczytanie go i wnioskowanie w oparciu o niego; Wiązanie obiektów odbywa się w czasie wykonania programu, w czasie kompilacji jeszcze niewiele wiadomo; Powiązane przykłady: wstrzykiwanie zależności, metadane umożliwiające przekształcanie obiektów w dane relacyjne i z powrotem, żarkusze stylówż; Programowanie refleksyjne: Mechanizm refleksji - mechanizm umożliwiający badanie i modyfikowanie programu w czasie jego wykonania; Refleksja pozwala na łatwą paramteryzajcę programu (szczególny przypadek programowanie sterowanego danymi); Kod wykorzystujący refleksję jest mniej czytelny i nie pozwala na sprawdzenie Poprawności składniowej i semantycznej w trakcie kompilacji; Zasada żnie rozmawiaj z obcymiż nakłada ograniczenie na obiekty, do których wolno wysłać komunikat w ramach metody: obiekt this;atrybut this; parametr metody;obiekt utworzony wewnątrz metody; Czasem wymaga dodania nowej publicznej metody żu znajomychż ukrywającej sposób pozyskania informacji: wzorce GoF: kreacyjne — tworzenie, inicjalizacja i konfiguracja obiektów (Singleton, Factory, Builder...); Strukturalne — struktury powiązań obiektów (Adapter, Composite. Facade. Proxy,...); Czynnościowe — zachowanie, współpraca obiektów (Observer, Strategy, Template Method, Command, State...);Adapter: Problemy: Jak zapewnić stabilny interfejs dla podobnych komponentów, które mają różne interfejsy?; Jak dopasować interfejs żstarejż klasy do nowych potrzeb bez wprowadzania zmian w kodzie? Rozwiązanie: Klasa adaptera implementuje wymagany interfejs i deleguje zadanie do odpowiedniej klasy, a następnie adaptuje jej odpowiedź.; GoF a GRASP: Wzorce GoF w dużej mierze opierają się na zasadach opisanych w GRASP (są ich specjalizacją); Dobrze znać wzorce, ale jeszcze lepiej jest zrozumieć ogólne zasady, czyli żalfabetż technik projektowych Factory: Problem:, Kto powinien odpowiadać za tworzenie obiektów: złożonej logice wytwórczej lub niezwiązanych z warstwą dziedzinową?; Np., kto powinien tworzyć adaptery (i decydować o wyborze)? Rozwiązanie: Utwórz abstrakcyjny obiekt (Pure Abstraction) nazywany fabryką i przydziel mu to zobowiązanie. Zalety: Przydział zobowiązania złożonej kreacji do spójnego, pomocniczego obiektu (High Cohesion); Ochrona przed zmianami w implementacji (np. projektowanie sterowane danymi); Umożliwia wprowadzenie strategii zarządzania pamięcią (np. recykling obiektów, przechowywanie ich w pamięci cache); Dostęp do fabryki często odbywa się zgodnie z zasadami opisanymi we wzorcu Singleton(jedna instancja widoczna globalnie): Wymagania: Potrzebujemy tylko jednej instancji klasy ServiceFactory; Metody ServiceFactory muszą być dostępne w wielu różnych miejscach kodu; Możliwe rozwiązania: Przekazywanie instancji ServiceFactory, jako parametru we wszystkich miejscach, w których dostęp do niej jest potrzebny; Inicjalizacja obiektów z użyciem trwałej referencji; Wzorzec Singleton ( dobre rozwiązanie!); Singleton – wywołanie: public class Register

{

public void initializeO

{

taxAdapter =

ServicesFactory .getlnstanceO .getTaxAdapterO ;

ServicesFactory jest publiczna, ma prywatny konstruktor; W dowolnym miejscu w kodzie w celu uzyskania dostępu do instancji klasy możemy napisać ServicesFactory.getlnstance(); Następnie możemy do tej instancji wysłać komunikat, czyli mamy globalne wywołanie getTaxAdapter(); Singleton vs. Static Class: W Singleton metody nie muszą być statyczne: możliwe dziedziczenie, polimorfizm; Metody nie-statyczne są bardziej uniwersalne (np. czasami okazuje się, ze potrzeba więcej niż 1 instancja); Ogólna zasada: Singleton: potrzeba jednego punktu do zarządzania, konfiguracji, jednej instancji klasy na każdą maszynę wirtualną Static Class:, gdy mamy grupę pomocniczych funkcji, które powinny być trzymane razem (np. klasy Math, Utils); Strategy: Problem: jak powinien wyglądać projekt, jeśli mamy wiele powiązanych algorytmów/strategii, których używamy zamiennie? Rozwiązanie: zdefiniuj każdy algorytm/strategię w osobnej klasie, klasy powinny mieć wspólny interfejs.; Factory of Strategies:, Kto powinien tworzyć strategie, jeśli istnieje wiele różnych i zmieniają się w czasie? Strategie może/powinna tworzyć dedykowana fabryka, która będzie potrafiła odczytać nazwę odpowiedniej klasy i ewentualne parametry z zewnętrznego źródła danych. Dzięki temu możliwe są dynamiczne zmiany strategii nawet w czasie działania programu. Composite: Problem: reprezentacja hierarchii klas, tak by liście i węzły pośrednie (kompozyty) mogły być traktowane w jednolity sposób Rozwiązanie: liście i węzły pośrednie implementują ten sam interfejs; Przykład: operacje copy, move, rename w systemie plików; Facade: Problem: redukcja sprzężenia między zbiorem powiązanych klas (podsystemów) a resztą systemu Rozwiązanie: klasa Facade dostarczająca wysokopoziomowy interfejs dla całego podsystemu; zastosowania: Dostarczenie prostego interfejsu dla złożonego podsystemu: Zmniejszenie sprzężenia między klientem a podsystemem; Kod klienta wykorzystującego fasadę jest czytelniejszy;

Zebranie kilku różnych API w jedno uporządkowane API: API mogą być może źle zaprojektowane; Adapter vs. Facade - podobne rozwiązania, różne intencje (Adapter - dostosowuje interfejsy do istniejącego systemu, Facade - upraszcza i organizuje interfejsy podsystemów);

Punkt wejściowy do podsystemu/warstwy: Wprowadzenie podziału aplikacji na warstwy, który ułatwia niezależny rozwój podsystemów; Można zablokować bezpośredni dostęp do podsystemu; Przykłady: Kontroler fasadowy - delegowanie zadań z Ul:, Gdy mamy wiele klas z różnymi metodami dostępu do bazy danych (np. dla różnych obiektów] można stworzyć fasadę prostym interfejsem [np. load() oraz get()); Model synchroniczny: Program ma do wykonania 3 zadania:

-jeden wątek, ustalona kolejność - jedno zadanie na raz;

-kolejne zadania mogą łatwo korzystać z wyników zadań wcześniejszych; Model wielowątkowy: Każde zadanie w osobnym wątku:

-z punktu widzenia programisty: niezależne sekwencje poleceńż, wykonywane równolegle;

- w praktyce zarządzanie wykonaniem/przełączaniem wątków zarządza OS (jeden rdzeń, wiele rdzeni, wiele procesorów,...);

-programy wielowątkowe bywają skomplikowane, ponieważ» wątki zazwyczaj musząą być wzajemnie skoordynowane; Model asynchroniczny: Zadania są _przeplatane_ w jednym wątku:

-zadanie jest wykonywane, dopóki jawnie nie odda kontroli (z punktu widzenia programisty łatwiejsze niż wielowątkowość)

-w modelu wielowątkowym może być przeplatanie, ale poza kontrolą programisty: decyduje OS (np. dla jednego rdzenia: _bez_, dla wielu: _z_)

-model asynchroniczny i wielowatkowy można stosowac jednocześnie; synchroniczny vs. Asynchroniczny: W ramach jednego wątku mogłoby się wydawać, że nie ma różnicy w czasie wykonania w modelu asyn. i syn.; Z punktu widzenia programisty model asyn. jest trudniejszy:

-trzeba zorganizować zadanie jako sekwencje kroków,;trzeba uwzględnić, że zadania mogł być od siebie zależne.; Po co nam zatem model asynchroniczny? np. by poprawić _responsywność GUI_ (czas wykonania taki sam, ale przyjemniej);gdy zadania są zmuszone czekać na pewne zdarzenie i blokują procesor (to jest bardzo istotne!!!); Model synchroniczny a blokowanie: Dlaczego zadania _blokują_ procesor? Np. czekają na zakończenie operacji I/O (z dysku, innego urządzenia, sieci ... ); Typowe CPU może przetwarzać dane znacznie szybciej niż są one dostarczane z dysku lub sieci; Główna idea stoj...

Zgłoś jeśli naruszono regulamin