Elementy projektowania obiektowego
Transkrypt
Elementy projektowania obiektowego
Object-oriented development • • • • Object-oriented analysis, design and programming are related but distinct OOA is concerned with developing an object model of the application domain OOD is concerned with developing an object-oriented system model to implement requirements OOP is concerned with realising an OOD using an OO programming language such as Java or C++ 1 Class Relationships B composition A Uses U Is-A client aggregation D client Object Relationships b ε B a ε A u ε U d ε D 2 Forwarding and delegation • • • Inheritance, as we saw it, is one way to define new functionality from existing functionality Inheritance can be tricky to use well, because it implies a tight binding between the original and new classes Two looser approaches, which are sometimes better, are forwarding and delegation: – “If object O1 does not understand message M, then it passes M to object O2” • Forwarding and delegation differ in how they treat self: – In forwarding, O1 and O2 keep separate identities: a self call in O2 stays in O2 – In delegation, there is just one identity, namely O1: a self call in O2 will call O1 (delegation implies a common self) 3 The three approaches Inheritance Delegation Forwarding Defined on classes Defined on objects Defined on objects Common self No common self No common self Tight binding between original and derived object/class Static approach: At class definition Loose binding Dynamic approaches: Can be defined at run-time 4 Bad Designs • What makes a design bad? Robert Martin suggests[1]: – Rigidity It is hard to change because every change affects too many other parts of the system. – Fragility When you make a change, unexpected parts of the system break. – Immobility It is hard to reuse a part of the existing software in another application because it cannot be disentangled from the current application. • The design principles discussed in class are all aimed at preventing “bad” design. 5 Principles • Liskov Substitution Principle – A pointer or reference to a base class may be replaced by a pointer or reference to a derived class without making any changes to its clients. – Supports the powerful hook idea – allow applications to specify what a specific library’s function call means. • Open/Closed Principle – Components should be open for extension but closed for modification. – Dynamic binding and templates are excellent means to accomplish this. • Dependency Inversion Principle – Policies and their Implementations should depend on shared abstractions, not on each other’s concrete details. – Programming to interfaces and using object factories are what’s needed. • Interface Segregation Principle – A using class should not have to bind to parts of an interface it doesn’t need. – Careful partitioning of classes, mixins, and multiple interfaces per class help support this principle. 6 Pojęcie wzorca projektowego • • • Wzorce projektowe „funkcjonują” w praktyce programistycznej od drugiej połowy lat 90. ubiegłego wieku (Design Patterns: Elements of Reusable Object-Oriented Software, E. Gamma, R. Helm, R. Johnson, J. Vlissides) W dziedzinie zwanej inżynierią oprogramowania stanowią abstrakcyjny opis zależności pomiędzy klasami ( pojęciami abstrakcyjnymi) W efekcie stosowania wzorców projektowych uzyskujemy pewną standaryzację kodu, tym samym czyniąc go bardziej zrozumiałym, efektywniejszym i mniej zawodnym Klasyfikacja wzorców projektowych Wzorce konstrukcyjne – do pozyskiwania obiektów zamiast bezpośredniego tworzenia obiektów klas (np., Budowniczy, Singleton, Prototyp) Wzorce strukturalne – pomagające łączyć obiekty w większe struktury udostępniając interfejs do ich obsługi (np. Adapter, Fasada, Dekorator,Proxy,Flyweigh ) Wzorce czynnościowe – definiujące komunikację pomiędzy obiektami oraz kontrolujące przepływ danych w złożonej aplikacji (np. Iterator, Obserwator, Strategia, Memento, Visitor, Mediator ) 8 Wzorce konstrukcyjne (5) Wzorce strukturalne (7) Wzorce czynnościowe (11) Singleton Intencja singletonu wg Bandy Czworga: „Zapewnienie, że klasa posiada tylko jedną instancję oraz dostarczenie globalnego punktu dostępu do tej instancji”. • Umożliwia utworzenie dokładnie jednej instancji danej klasy i nie wymaga od obiektów użytkownika, by „wiedziały”, czy instancja ta została już utworzona 12 Implementacja Singleton 1. Specjalna metoda sprawdza czy obiekt został już utworzony a) b) 2. Tworzy instancję i zwraca do niej referencję Zwraca referencję do wcześniej utworzonej instancji Konstruktor deklaruje się jako metodę o dostępie chronionym lub prywatnym (aby zapewnić, że wywołanie tej metody jest jedynym sposobem utworzenia obiektu) 13 Struktura singletonu Użytkownik tworzy instancję singletonu wyłącznie za pomocą metody pobierzInstancje zwraca instancję Konstruktor deklarujemy jako metodę o dostępie prywatnym aby nie można się było nim posłużyć do utworzenia większej liczby instancji przez jego bezpośrednie wywołanie Singleton - static instancja - daneSingletonu + static pobierzInstancje () + SingletonOperacja () + pobierzDaneSingletonu () 14 Implementacja klasy w C++ class Singleton { public: static Singleton* Instance(); protected: Singleton(); private: static Singleton* _instance; }; Singleton* Singleton::_instance = 0 ; Singleton* Singleton::Instance () { if (_instance == 0) { instance = new Singleton; } return _instance; } 15 Singleton jako Template template <class TYPE> class Singleton { public: // Global access point static TYPE *instance (void); template <class TYPE> TYPE * Singleton::instance_ = 0; template <class TYPE> TYPE * Singleton::instance (void) { protected: // Default constructor. Singleton (void); if (Singleton<TYPE>::instance_ == 0) { Singleton<TYPE>::instance_ = new Singleton<TYPE>; } return Singleton<TYPE>::instance_; // Contained instance. TYPE instance_; }; }; 16 Singleton (Java) • Singleton to klasa, która może mieć tylko jedną instancję (obiekt) class Singleton { private Singleton() { } private static Singleton S; public static Singleton getInstance() { if (S == null) S = new Singleton(); return S; } } Singleton a = new Singleton(); Singleton b = Singleton.getInstance(); Singleton c = Singleton.getInstance(); 17 Wzorzec strukturalny: Adapter • • Zadaniem tego wzorca jest przetworzenie interfejsu danej klasy w inny interfejs, którego oczekuje Klient Adapter umożliwia współpracę klasom o niekompatybilnych interfejsach 18 Adapter: Intencja Adaptera wg Bandy Czworga: „Dostosowanie interfejsu klasy do interfejsu, którego oczekuje użytkownik. Adapter umożliwia współprace klas, która bez jego zastosowania nie byłaby możliwa ze względu na ich niezgodne interfejsy.” 19 Adapter Wzorzec Adapter konwertuje interfejs jednej klasy na interfejs innej klasy. Używamy tego wzorca, jeśli chcemy, żeby dwie niezwiązane ze sobą klasy współpracowały ze sobą w jednym programie. Koncepcja wzorca Adaptera jest bardzo prosta: piszemy klasę, która posiada wymagany interfejs, a następnie zapewniamy jej komunikację z klasą, która ma inny interfejs. Istnieją dwa sposoby realizacji: poprzez dziedziczenie i poprzez kompozycję. 20 Adapter obiektów Pierwotną klasę zawieramy wewnątrz nowej i tworzymy metody wymaganego interfejsu realizujące wywołania metod klasy wewnętrznej. Client Target Adaptee Request() SpecificAdaptee() Adapter Request() adaptee adaptee->SpecificRequest() 21 Przykład : adapter obiektów Klient Figura XXOkrag Wyswietl() Wypelnij() Usun() Wyswietlaj() Wypelnijaj() Usuwaj() 1 1 Punkt Linia Kwadrat Okrag Wyswietl() Wypelnij() Usun() Wyswietl() Wypelnij() Usun() Wyswietl() Wypelnij() Usun() Wyswietl() Wypelnij( ) Usun() xxokrag->Wyswietlaj () xxokrag->Wypeniaj() xxokrag->Usuwaj 22 Adapter klas Z klasy, która ma niezgodny interfejs wywodzimy klasę pochodną i dopisujemy nowe metody tak, by uzyskać wymagany interfejs. Client Target Adaptee Request() SpecificAdaptee() Adapter Request() SpecificRequest() 23 Przykład: adapter klas Client Rectangle AdapteeRectangle x1_, y1_, x2_, y2_ : int Draw() : void OldDraw():void RectangleAdapter x1, y1, width, height : int Draw() : void OldDraw() 24 Wzorzec strukturalny: Fasada • • • • Wzorzec ten jest pomyślany jako środek dostępu do złożonego systemu prezentujący na zewnątrz uproszczony lub uporządkowany interfejs programistyczny. Wzorzec ten definiuje interfejs wyższego poziomu, sprawiający, że łatwiej jest używać podsystemów. Dobrym przykładem zastosowania Fasady jest aplikacja bankomatowa – od strony urządzenia (bankomat) mamy ograniczony dostęp do systemu bankowego, obsługiwany przez odpowiednie metody Model Fasady jest dwuczęściowy – interfejs zewnętrzny, który wie, który z podsystemów odpowiada za konkretne zadanie i wyśle we właściwe miejsce żądanie wykonania – podsystemy, przejmujące jedynie zadanie zlecone przez interfejs zewnętrzny Wzorzec czynnościowy: Obserwator • Wzorzec obserwatora służy do powiadamiania wielu obiektów (obserwatorów) o zmianie w obiekcie – przedmiocie obserwacji Example of the Observer Pattern a b c x 6030 10 y 503020 z 8010 10 a b c a b c a = 50% b = 30% c = 20% requests, modifications change notification 27 Structure of the Observer Pattern Subject Attach(Observer) Detach(Observer) Notify() observers for all o in observers { o -> Update()} Observer Update() ConcreteObserver ConcreteSubject GetState() SetState() subject Update() observerState = subject->GetState() observerState return subjectState subjectState 28 Wzorzec czynnościowy: Iterator • Intent: – Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation 29