Inynieria oprogramowania Wykad 6 Projektowanie oprogramowania Bartosz Walter
Inżynieria oprogramowania Wykład 6 Projektowanie oprogramowania Bartosz Walter <Bartosz. Walter@cs. put. poznan. pl>
Agenda § Czy oprogramowanie jest zawsze projektowane? § Znaczenie projektowania § Projektowanie a wyniki fazy analizy § Składowe projektowania
Zadania w fazie projektowania § Uszczegółowienie wyników analizy § Projektowanie technicznych składowych oprogramowania innych niż składowa dziedziny problemu § Poprawa efektywności systemu § Definicja architektury systemu § Wybór technologii
Uszczegółowienie wyników analizy § Dopracowanie diagramu klas § Opracowanie diagramów sekwencji § Wprowadzenie związków skierowanych § Określenie sposobu implementacji związków § Dobór typów danych § Opracowanie sygnatur metod § Określenie algorytmów metod
Kryteria jakości projektu obiektowego § Modularność § § § Wysoka spójność Słabe sprzężenia Prostota hierarchii dziedziczenia Prostota klas i obiektów Prostota protokołów i usług § Przejrzystość § Modularność § Nazewnictwo – proste, spójne, precyzyjne § Złożoność składowych
Spójność obiektowa § Obiekt jest spójny jeżeli wszystkie jego składowe służą do realizacji jednego celu § Spójność mierzy się stopniem wykorzystania pól obiektu przez jego własne metody § Celem jest sytuacja, w której odwołania do pól występują jedynie z metod danej klasy lub jej podklasy
Sprzężenie Zrozumienie jednej składowej nie powinno prawie nigdy wymagać zrozumienia innych składowych Sprzężenie między dwoma klasami/obiektami opisuje zależność pomiędzy nimi. Klasa A jest powiązana z klasą B, jeżeli A musi posiadać pewną wiedzę o B: § § jako jej pole składowe, jako implementacja interfejsu, jako podklasa, jako parametr, typ metody albo wyjątek.
Projektowanie składowych technicznych § Składowe techniczne mogą być projektowane jak składowa dziedziny problemy, czyli Rzeczywistość Model Projekt § W odróżnieniu od dziedziny problemu, składowe techniczne są powtarzalne § Dlatego stosuje się § § § Gotowe biblioteki Generatory kodu Wzorce architektoniczne Wzorce projektowe Zasady tworzenia interfejsu użytkownika
Projektowanie składowej interfejsu użytkownika § Klasyfikacja ludzi § kim są użytkownicy systemu § Opis scenariuszy ich zadań § czego oczekują od systemu, kim są? § Hierarchia poleceń § zasada 7± 2 albo 3 x 3 § Projektowanie szczegółowej interakcji § minimalizacja kliknięć i przyciśnięć klawisza § brak "ciszy w eterze" § Prototypy
Projektowanie składowej zarządzania danymi § Fizyczna struktura serwerów danych § Mechanizm dostępu do danych § Dokumenty vs. pojedyncze dane § Bazy danych vs. pliki § Stopień dekompozycji danych § Zapis na bieżąco vs. zapis na żądanie § Kwestie optymalizacyjne (indeksy, partycjonowanie)
Wzorce projektowe "Wzorzec opisuje problem, który pojawia się wielokrotnie w danym środowisku, i opisuje podstawę rozwiązania tego problemu, w taki sposób, że można użyć tego rozwiązania milion razy, bez konieczności ponownego wykonywania tych samych czynności" Christopher Alexander "A Pattern Language", 1977
Wzorce w budownictwie lądowym Czy zbudujemy most, opierając przęsło na kolejnych filarach połączonych łukiem, tak aby łuk usztywniał i naciągał przęsło stanowiąc jego podparcie na całej długości przęsła, czy też mocując płyty z obu stron za pomocą kilku lin stalowych o kolejno coraz krótszych długościach do pylonów umieszczonych pośrodku długości mostu?
Wzorce w budownictwie lądowym Czy zbudujemy most łukowy czy podwieszany?
Atrybuty wzorca kiedy? jak? zyski/straty? Wzorzec kontekst? warunki wstępne? alternatywy?
Wzorce w inżynierii oprogramowania "Wzorzec projektowy identyfikuje i specyfikuje pewną abstrakcję, której poziom znajduje się powyżej poziomu abstrakcji pojedynczej klasy, instancji lub komponentu" E. Gamma et al. , 1993
Systematyka wzorców projektowych Kreacyjne § abstract the instantiation process § make the system independent of how the objects are created Strukturalne § how the classes are composed § use inheritance or composition appropriately Behawioralne § deal with algorithms and assignment of responsibilities § characterize control flow & interaction
Observer: Cel Zdefiniowanie pomiędzy obiektami zależności jeden-do-wielu, dzięki której jeżeli jeden (nadrzędny) obiekt zmieni stan, pozostałe także zostaną o tym powiadomione i zaktualizowane
Observer: Struktura
Observer: Uczestnicy § Subject § § zna swoje obserwatory posiada interfejs pozwalający dołączać i odłączać obserwatory § Observer § definiuje ogólny interfejs dla obserwatorów § Concrete Subject § § przechowuje stan, który obserwują obserwatory wysyła powiadomienia do obserwatorów § Concrete Observer § § dołącza i odłącza siebie do/od obiektu obserwowanego aktualizuje swój stan przy zmianach w obiekcie obserwowanym
Observer: Konsekwencje § ułatwione utrzymanie spójności systemu § abstrakcyjne powiązanie pomiędzy obiektami Subject i Observer § § Subject posiada znikomą wiedzę o swoich obserwatorach Subject i obserwatory mogą należeć do różnych warstw aplikacji § programowy broadcasting § skalowalność aktualizacji obserwatorów § § push: obserwatory otrzymują pełną informację o zmianie (czy jej potrzebują, czy nie) pull: obserwatory otrzymują tylko powiadomienie i muszą zapytać Subject o szczegóły § "wiszące" referencje po stronie obiektu
Observer: Przykład Rachunek klienta może być powiązany z innymi rachunkami pobocznymi. Na przykład, z głównym rachunkiem jest związany rachunek kredytu odnawialnego. Wysokość otwartej linii kredytowej zależy od środków na rachunku głównym. Zmiana wysokości salda na tym rachunku skutkuje podwyższeniem lub obniżeniem dostępnej kwoty kredytu.
Factory Method: Cel § Zdefiniowanie interfejsu do tworzenia obiektu § Odsunięcie tworzenia obiektów określonej klasy do podklas § Podklasy decydują której klasy i którego konstruktora użyć do utworzenia obiektu produktu.
Factory Method: Zastosowanie § Klient nie może przewidzieć, jakiej klasy obiekt należy utworzyć § Klasa chce, aby jej podklasy określały jaki obiekt utworzyć; w tym celu przekazuje im część swojej odpowiedzialności
Factory Method: Struktura by the Gang of
Factory Method: Uczestnicy § Product § deklaruje interfejs dla obiektów utworzonych przez Factory Method § Concrete. Product § implementuje interfejs Product § Creator § deklaruje interfejs dla metody fabryki obiektów typu Product § może posiadać domyślną implementację metody fabryki § Concrete. Creator § pokrywa metodę fabrykę, tak aby zwracała obiekt
Factory Method: Konsekwencje § przesunięcie akcentu na podklasy § podklasy w ich metodach fabrykach mogą tworzyć zmienioną lub rozszerzoną wersję produktu § parametryzowane FM vs. polimorficzne FM § Creator może wybrać obiekt, który chce utworzyć, lub przekazać tę odpowiedzialność do podklas § dwa poziomy wyboru: podklasy, która utworzy specyficzny produkt, oraz konstruktora wewnątrz podklasy
Factory Method: Przykład W zależności od wybranego sposobu drukowania wyciągów, są one tworzone codziennie, cotygodniowo lub comiesięcznie. Każdy z nich jest reprezentowany przez oddzielny obiekt.
Chain of Responsibility: Cel § Uniknięcie wiązania nadawcy żądania z jego odbiorcom poprzez utworzenie grupy potencjalnych odbiorców, którzy mogą obsłużyć żądanie § Odbiorcy są ułożeni w łańcuch i przekazują sobie żądanie wzdłuż łańcucha aż jeden z nich obsłuży je.
Chain of Responsibility: Structure by the Gang of
Chain of Responsibility: Participants § Handler § § defines an interface for handling requests (optional) implements the successor link § Concrete Handler § § § handles requests it is responsible for can access its successor if the Concrete. Handler can handle the request, it does so; otherwise it forwards the request to its successor § Client § initiates the request to a Concrete. Handler object on the chain by the Gang of
Chain of Responsibility: Consequences Chain of Responsibility allows for: § reduced coupling § § § Object does not need to know which other object handles the request both Sender and Receiver have no knowledge of each other chain elements do not know the chain's structure § added flexibility in assigning responsibilities to objects § responsibilities can be freely distributed among chain elements § no guarantee of receipt by the Gang of
Chain of Responsibility: Przykład 1
Chain of Responsibility: Przykład 1
Chain of Responsibility: Przykład 2
Chain of Responsibility: Przykład 2
State: Cel Umożliwienie obiektowi zmiany jego zachowania w odpowiedzi na zmianę jego wewnętrznego stanu. Obiekt będzie pozornie zmieniał swoją klasę.
State: Zastosowanie § Zachowanie obiektu zależy od jego stanu i podlega zmianom w trakcie wykonywania programu § Wewnątrz metody istnieją złożone, wieloczęściowe wyrażenia warunkowe, które zależą od stanu obiektu § Każdy stan jest oddzielnym obiektem, który może zmieniać się niezależnie od innych obiektów
State: Struktura
State: Uczestnicy § Context § § defines the interface of interest to clients maintains an instance of a Concrete. State subclass that defines the current state § State § defines an interface for encapsulating the behavior associated with a particular state of the Context § Concrete State subclasses § each subclass implements a behavior associated with a state of the Context
State: Konsewencje § partition of the behavior for different states § § state-specific code lives in State subclasses intent of the state-dependent behavior is clearer § explicit state transitions § protection from inconsistent internal states § possible sharing of state objects § States are stateless
State: Przykład Rachunek może znajdować się w stanie otwartym lub zamkniętym. W stanie otwartym właściciel może dokonywać wpłat na rachunek, podczas gdy w stanie zamkniętym stan rachunku nie może ulec zmianie.
State: Przykład public class Account { private int balance = 0; private String owner = null; private boolean is. Open = false; public Account(String owner, int balance) { this. owner = owner; this. balance = balance; this. Open = true; } public void credit(int amount) { if (is. Open) { balance += amount; } else { alert("The account is closed!"); } } }
State: Przykład public interface Account. State { public void credit(Account acc, int amount); } public class Account. Open implements Account. State { public void credit(Account acc, int amount) { acc. balance += amount; } } public class Account. Closed implements Account. State { public void credit(Account acc, int amount) { alert("The account is closed!"); } }
State: Przykład public class Account { private int balance = 0; private String owner = null; private Account. State state = null; public Account(String owner, int balance) { this. owner = owner; this. balance = balance; this. state = new Account. Open(); } public void credit(int amount) { this. state. credit(this, amount); } public void close() { this. state = new Account. Closed(); } }
Decorator: Cel § Umożliwienie dynamicznego dołączania odpowiedzialności do obiektu § Dostarczenie elastycznej alternatywy dla dziedziczenia w celu rozszerzania funkcjonalności
Decorator: Problem public class Invoice { String buyer = null; String issuer = null; List <List. Item> elements = new Array. List<List. Item>(); Header header = null; private boolean use. Header() { return header != null; } public void print() { if (use. Header()) { header. print(); } print("Issuer: " + issuer); print("Buyer: " + buyer); } } for (e : elements) { e. print(); }
Decorator: Problem public class Invoice { String buyer = null; String issuer = null; List <List. Item> elements = new Array. List<List. Item>(); public void print() { print("Issuer: " + issuer); print("Buyer: " + buyer); for (e : elements) { e. print(); } } } public class Header. Invoice extends Invoice { Header header = null; } private boolean use. Header() { return header != null; } public void print() { if (use. Header()) { header. print(); } super. print(); }
Decorator: Problem
Decorator: Struktura
Decorator: Uczestnicy § Component § deklaruje interfejs dla obiektów, którym można zwiększyć zakres odpowiedzialności § Concrete Component § implementuje interfejs Component § Decorator § § posiada interfejs zgodny z interfejsem Component jest świadom istnienia dekorowanego Componentu (który jest instancją Concrete Component lub innym Decoratorem) § Concrete Decorator § dodaje odpowiedzialność do komponentu
Decorator: Konsekwencje § większa elastyczność niż w przypadku dziedziczenia § § odpowiedzielność może być zwiększana dynamicznie mniej złożona hierarchia klas § pay-as-you-go § poszczególne cechy są dodawane na żądanie § różnica w tożsamości obiektów § tożsamość obiektu jest inna od tożsamości dekoratora § tożsamość obiektu powinna być hermetyzowana i ukryta przed klientem § duża liczna małych, dobrze zdefiniowanych
Decorator: Przykład
Flyweight: Cel § Ponowne użycie i współdzielenie obiektów w celu zwiększenia efektywności ich wykorzystania § Oddzielenie wewnętrznego (współdzielnonego) i zewnętrznego (unikatowego) stanu obiektu
Flyweight: Struktura
Flyweight: Uczestnicy § Flyweight § deklaruje interfejs, przez który obiekty mogą otrzymywać swój stan zewnętrzny § Concrete Flyweight § musi być niezależny od swojego kontekstu (stanu zewnętrznego) § Unshared Concrete Flyweight § specjalny przypadek obiektu niewspółdzielnonego § Flyweight Factory § § tworzy i zarządza obiektami Flyweight zapewnia poprawność procesu zarządzania obiektami § Client § otrzymuje instancje Flyweight poprzez Flyweight. Factory
Flyweight: Konsekwencje § rosnące oszczędności pamięci § § § ograniczenie całkowitej liczby instancji ograniczenie współdzielonego stanu wewnętrznego stan zewnętrzny może być wyliczony lub zapamiętany § wzrost kosztu obliczeniowego § zarządzanie stanem zewnętrznym
Flyweight: Przykład public abstract class Tea. Order { // Flyweight public abstract void serve. Tea(Tea. Order. Context tea. Order. Context); } public class Tea. Flavor extends Tea. Order { String tea. Flavor; // stan wewnętrzny // Concrete. Flyweight Tea. Flavor(String tea. Flavor) { this. tea. Flavor = tea. Flavor; } public String get. Flavor() { return this. tea. Flavor; } } // tea. Order. Context jest zewnętrznym stanem public void serve. Tea(Tea. Order. Context tea. Order. Context) { System. out. println("Serving tea flavor " + tea. Flavor + " to table number " + tea. Order. Context. get. Table()); }
Flyweight: Przykład public class Tea. Order. Ctx { int table. Number; // kontekst dostarcza stan zewnętrzny Tea. Order. Context(int table. Number) { this. table. Number = table. Number; } } public int get. Table() { return this. table. Number; }
Flyweight: Przykład public class Tea. Flavor. Factory { // Flyweight Factory Tea. Flavor[] flavors = new Tea. Flavor[10]; // <10 flavors can be made int teas. Made = 0; // Factory Method public Tea. Flavor get. Tea. Flavor(String flavor. To. Get) { for (int i = 0; i < teas. Made; i++) { if (flavor. To. Get. equals((flavors[i]). get. Flavor())) { return flavors[i]; } } flavors[teas. Made] = new Tea. Flavor(flavor. To. Get); return flavors[teas. Made++]; } } public int get. Total. Tea. Flavors. Made() { return teas. Made; } by Lawrence Trurett
Flyweight: Przykład class Test. Flyweight { static Tea. Flavor[] flavors = new Tea. Flavor[100]; // zamówienia static Tea. Order. Ctx[] tables = new Tea. Order. Ctx[100]; // stoły static int orders. Made = 0; static Tea. Flavor. Factory tea. Flavor. Factory; static void take. Orders(String flavor. In, int table) { flavors[orders. Made] = tea. Flavor. Factory. get. Tea. Flavor(flavor. In); tables[orders. Made++] = new Tea. Order. Ctx(table); } } public static void main(String[] args) { tea. Flavor. Factory = new Tea. Flavor. Factory(); take. Orders("chai", 2); take. Orders("camomile", 1); take. Orders("earl grey", 1); take. Orders("camomile", 897); for (int i = 0; i < orders. Made; i++) { flavors[i]. serve. Tea(tables[i]); } System. out. println(" "); System. out. println("total tea. Flavor objects made: " + tea. Flavor. Factory. get. Total. Tea. Flavors. Made()); } }
Command: Cel § Hermetyzacja żądania jako obiektu § Umożliwienie parametryzacji klientów różnymi żądaniami § Wsparcie dla żądań odwracalnych
Command: Struktura
Command: Uczestnicy § Command § deklaruje interfejs dla wykonywania zadania (execute()) § Concrete. Command § § definiuje powiązanie pomiędzy Receiverem i akcją implementuje metodę execute() § Client § tworzy obiekt Concrete. Command i wskazuje jego odbiorcę (Receiver) § Invoker § § wywołuje obiekt Command w celu wykonania żądania Receiver
Command: Konsekwencje § usunięcie powiązania nadawcy i odbiorcy (Receiver) § obiekty Command mogą być składane w polecenia złożone § dodawanie nowych obiektów Command jest łatwe § Command można łatwo rozszerzyć o odwracanie ich działania
Command: Przykład
Command: Przykład public class Bank { // Invoker, Client public void income(Account acc, long amount) { Operation oper = new Income(amount); acc. do. Operation(oper); } } public void transfer(Account from, Account to, long amount) { Operation oper = new Transfer(to, amount); from. do. Operation(oper); } public class Account { // Reciever long balance = 0; Interest interest = new Interest. A(); History history = new History(); } public void do. Operation(Operation oper) { oper. execute(this); history. log(oper); }
Command: Przykład abstract public class Operation { public void execute(); } // Command public class Income { // Concrete. Command 1 public Income(long amount) { // store parameters. . . } } public void execute(Account acc) { acc. add(amount); } public class Transfer { // Concrete. Command 2 public Income(Account to, long amount) { // store parameters. . . } } public void execute(Account from) { from. subtract(amount); to. add(amount); }
Visitor: Cel § Reprezentacja operacji, która ma być wykonana na elementach heterogenicznej struktury. § Umożliwienie zdefiniowania nowej operacji bez zmiany samych elementów, na których ona operuje
Visitor: Struktura
Visitor: Uczestnicy § Visitor § deklaruje sposób realizacji operacji dla każdego Concrete. Element, który ma być odwiedzony § Concrete Visitor § implementuje konkretną operację § Element § definiuje metodę accept(), która otrzymuje obiekt Visitor jako parametr § Object Structure § zwraca pojedyncze elementy struktury § może posiadać interfejs wysokiego poziomu który pozwala obiektom Visitor odwiedzać elementy
Visitor: Konsekwencje § § łatwe dodawanie nowych operacji (Visitorów) trudne dodawanie nowych elementów (Concrete. Elements) § każdy Concrete. Element wymaga stworzenie nowej metody we wszystkich istniejących Visitorach § obsługa obiektów niezależna od ich klas § inaczej niż Iterator, Visitor może odwiedzać obiektów różnych klas
Visitor: Konsekwencje (cd. ) § akumulacja stanu § Visitory mogą akumulować stan odwiedzanych elementów w sposób dla nich specyficzny § konieczność naruszenia hermetyzacji § z uwagi na odwrócenie sterowania pomiędzy elementem a Visitorem, konieczne jest udostępnienie im nazwajem metod accept() i visit*()
Visitor: Przykład public class Bank { List<Banking. Product> products = new Array. List<Banking. Product>(); public List<Banking. Product > do. Report(Report report) { List<Banking. Product > result = new Array. List<Banking. Product >(); for (Banking. Product product : products) { result. add(product. accept(report)); } } } return result;
Visitor: Przykład public abstract class Banking. Product { } public interface Element { public Banking. Product accept(Report report); } public class Account extends Banking. Product implements Element { public Banking. Product accept(Report report) { if (is. Priviliged(report)) { return report. visit(this); } } } return null; public class Credit extends Banking. Product implements Element { public Banking. Product accept(Report report) { if (is. Priviliged(report)) { return report. visit(this); } } } return null;
Visitor: Przykład public class Over 1000 Report implements Visitor { public Banking. Product visit(Account acc) { if (acc. balance > 1000) return this; return null; } } public Banking. Product visit(Credit credit) { if (credit. draft > 1000 && credit. is. Active()) return this; return null } public class Pass. All. Report implements Visitor { public Banking. Product visit(Account acc) { return this; } } public Banking. Product visit(Credit credit) { return this; }
Readings 1. Gamma E. et al. , Design Patterns. Elements of Reuseable Object. Oriented Software. Addison. Wesley, 1995 2. Eckel B. , Thinking in patterns. http: //www. bruceeckel. com 3. Cooper J. , Java. Wzorce Projektowe. Helion, 2001 4. Shalloway A. , Trott J. , Projektowanie zorientowane obiektowo. Wzorce projektowe. Helion, 2001
Q&A
- Slides: 84