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