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 Is-A client aggregation D Uses U client b ε B Object Relationships 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 Loose binding Static approach: At class definition 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) Tworzy instancję i zwraca do niej referencję b) Zwraca referencję do wcześniej utworzonej instancji 2. 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ę Singleton - static instancja - danesingletonu 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 + 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); protected: // Default constructor. Singleton (void); // Contained instance. TYPE instance_; }; template <class TYPE> TYPE * Singleton::instance_ = 0; template <class TYPE> TYPE * Singleton::instance (void) { if (Singleton<TYPE>::instance_ == 0) { Singleton<TYPE>::instance_ = new Singleton<TYPE>; } return Singleton<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 adaptee Request() adaptee->specificrequest() 21
Przykład : adapter obiektów Klient Figura Wyswietl() Wypelnij() Usun() XXOkrag 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 Draw() : void AdapteeRectangle x1_, y1_, x2_, y2_ : int 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 603010 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 ttach(observer) etach(observer) Notify() observers for all o in observers { o -> Update()} Observer Update() ConcreteSubject GetState() SetState() subjectstate subject ConcreteObserver Update() observerstate return subjectstate observerstate = subject->getstate() 28
Wzorzec czynnościowy: Iterator Intent: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation 29