Obiektowe metody projektowania systemów
The Composite
DESIGN PATTERN
Plan:
Relacja Agregacji
Relacja Kompozycji
Intencja wzorca Composite
Przykład zastosowania
Kiedy używać Composite pattern?
Struktura
Składniki struktury
Współpraca i konsekwencje
Problem implementacji
Prosty kod
Podsumowanie
Bibliografia
Relacja „composition”
Relacja „aggregation”
Intencja wzorca
Jednakowe traktowanie przez użytkownika
pojedynczych obiektów
kompozycji kilku obiektów
Komponowanie obiektów do struktury drzewa
Budowanie na zasadzie rekursywnej agregacji
Przykład zastosowania
Kiedy używać Composite pattern ?
1) Kiedy chcemy przedstawić obiekty w
hierarchii part-whole, czyli w postaci drzewa
2) Kiedy chcemy, żeby użytkownik traktował
jednakowo zarówno jeden obiekt, jak
również kontener zawierający kilka
obiektów
Struktura
Składniki struktury
Component (Graphic)
Stanowi interfejs obsługi obiektów drzewa
Implementuje defaultowe zachowania dla wszystkich obiektów
Deklaruje interfejs metod obsługi „dzieci”
Leaf (Line, Rectangle)
Reprezentuje obiekty bazowe nie posiadające „dzieci”
Definiuje zachowanie obiektów podstawowych
Composite (Group)
Definiuje zachowanie dla komponentów posiadających „dzieci”
Przetrzymuje komponenty
Implementuje metody obsługi „dzieci”
Client
Manipuluje obiektami poprzez interfejs klasy Component
Współpraca i konsekwencje
Client używa interfejsu klasy Component do operowania obiektami. Gdy odwołuje się do :
Leaf – żądanie odnosi się bezpośrednio do obiektu
Composite – następuje przekierowanie żądania do obiektów „dzieci”, wykonując przy tym dodatkowe operacje przed i/lub po przekazaniu żądania
Konsekwencje:
Obiekt Composite jest dla klienta również obiektem bazowym
Kod klienta zostaje uproszczony poprzez jednakowe traktowanie obiektów
Dodanie nowego rodzaju klasy Component nie wymaga zmiany kodu klienta
Trudności z ograniczeniem rodzajów elementów mogących składać się na obiekt Composite
Dodanie nowej metody wymaga zmiany we wszystkich klasach
Problem implementacji
Wyraźne referencje „rodzica”: utrzymywanie relacji „dziecko- rodzic” upraszcza przejścia i zarządzanie w strukturze
Współdzielenie komponentów: zmniejsza zasoby pamięci ale komponent nie ma już unikalnego „rodzica”
Metody obsługi „dzieci”: mają sens tylko dla obiektów
Composite ale są deklarowane dla wszystkich komponentów.
Imlementacja defaultowa metod AddChild() i RemoveChild() zgłasza wyjątek przy niewłaściwym posługiwaniu się
komponentami np. próba dodania elementu do obiektu nie
mogącego posiadać „dzieci”
Prosty kod: < Component >
class Graphic {
public:
Graphic();
virtual ~Graphic;
virtual void Draw();
virtual void AddChild(Graphic g)
virtual void RemoveChild(Graphic g) virtual Graphic GetChild(int i)
virtual int ChildCount()
}
Prosty kod c.d.:< Leaf >
class Rectangle public: Graphic {
public:
int x, y, w, h;
Rectangle( int newX, int newY, int newW, int newH) {x=newX; y=newY; w=newW; h=newH; }
void Draw() { g.drawRectangle(x, y, w, h); } }
class Line public: Graphic {
public:
int x0, y0, x1, y1;
Line( int newX0, int newY0, int newX1, int newY1) {x0=newX0; y0=newY0; x1=newX1; y1=newY1; }
void Draw() { g.drawLine (x0, y0, x1, y1); }
Prosty kod c.d.: < Composite >
class Group extends Graphic {
public:
Vector GraphicGroup = new Vector();
void Draw() {
for(int i=0; i<GraphicGroup.size(); i++) {
((Graphic)GraphicGroup.elementAt(i)).Draw();
} }
void AddChild(Graphic g) { GraphicGroup.addElement(g); }
void RemoveChild(Graphic g) { GraphicGroup.removeElement(g); } Graphic GetChild(int i) { return GraphicGroup.elementAt(i); }
int ChildCount() { GraphicGroup.size(); } }
Prosty kod c.d.: < Client >
class Program {
public:
static void main(String args[]) {
Group picture=new Group();
picture.AddChild(new Line(0,0,10,10));
picture.AddChild(new Rectangle(0,0,10,10));
picture.Draw();
}
}
Podsumowanie:
Composite pattern pozwala użytkownikowi traktować podstawowe obiekty (leafs) oraz ich kompozycje
(composites) jednakowo
Wszystkie komponenty posiadają podobny interfejs
Obiekty typu Composite wykonują operacje poprzez delegowanie akcji do swoich komponentów (children)
Pozwala na konstruowanie rekursywnych struktur o dowolnej złożoności
Obiekty typu Composite mogą mieć z góry określoną, jak również dowolną liczbę komponentów
Dodanie nowego komponentu sprowadza się wyłącznie do dodania nowej klasy
Dodanie nowej metody wymaga zmiany we wszystkich
klasach, chyba że zaimplementowany jest pattern Visitor
Klasa abstrakcyjna
class Equipment {
public:
Equipment(){};
Equipment(const char* );
virtual ~Equipment();
const char* Name();
virtual void SetPrice(double);
virtual double ShowPrice();
virtual void Add (Equipment* );
virtual bool Remove(Equipment* );
private:
const char* name;
double price;
};
Implementacja
Equipment::Equipment(const char* nazwa) { this->name = nazwa;}
Equipment::~Equipment() {}
const char* Equipment::Name() { return name;}
double Equipment::ShowPrice() { return 0;}
void Equipment::SetPrice(double price) {
cout<<"Ustawienie ceny jest niewożliwe"<<endl;
}
void Equipment::Add(Equipment* e) {
cout<<"Proba dodania elementu jest niemozliwa"<<endl;
}
bool Equipment::Remove(Equipment* e) { return 0; }
Klasa Composite
#include <list>
using namespace std;
class Equipment_Composite : public Equipment {
public:
Equipment_Composite(){};
Equipment_Composite(const char*);
virtual ~Equipment_Composite();
const char* Name();
double ShowPrice();
void Add (Equipment* );
bool Remove(Equipment* );
private:
const char* name;
list<Equipment*> ChildList;
list<Equipment*>::iterator i;
Implementacja
Equipment_Composite::Equipment_Composite(const char* nazwa) { this->name=nazwa; } Equipment_Composite::~Equipment_Composite() {}
void Equipment_Composite::Add(Equipment* e) { ChildList.push_front(e); } bool Equipment_Composite::Remove(Equipment* e)
{
if(e!=NULL) {
ChildList.remove(e);
return 1;
}
else return 0;
}
const char* Equipment_Composite::Name() { return name; } double Equipment_Composite::ShowPrice()
{
double total=0;
for (i = ChildList.begin(); i != ChildList.end(); ++i) total += (*i)->ShowPrice();
return total;
}
Liście:FloppyDisk,HardDrive,Monitor,Memory
class FloppyDisk : public Equipment {
public:
FloppyDisk(const char*);
virtual ~FloppyDisk();
const char* Name();
double ShowPrice();
void SetPrice(double);
private:
const char* name;
double price;
};
Implementacja
FloppyDisk::FloppyDisk(const char* nazwa) {
this->name=nazwa;
price=0;
}
FloppyDisk::~FloppyDisk() {}
const char* FloppyDisk::Name() { return name; } double FloppyDisk::ShowPrice()
{
if(price==0) {
cout<<"Nie ustalono jeszcze ceny"<<endl;
return 0;
}
else return price;
}
void FloppyDisk::SetPrice(double cena) { this->price=cena; }
Kod użytkownika
int main(int argc, char* argv[]) {
FloppyDisk* first= new FloppyDisk("Pen Drive");
first->SetPrice(10);
cout<<"Cena "<<first->Name()<<" wynosi "<<first->ShowPrice()<<endl;
Memory* second= new Memory( "512 DDRAM");
HardDrive* third= new HardDrive( "40GB ");
Monitor* fourth= new Monitor( "17' LCD ");
Equipment_Composite* Komputer= new Equipment_Composite("Komputer");
Komputer->Add(first);
Komputer->Add(second);
Komputer->Add(third);
cout<<"Cena "<<Komputer->Name()<<" wynosi "<<Komputer->ShowPrice()<<endl;
Equipment_Composite* Zestaw= new Equipment_Composite("Zestaw");
Zestaw->Add(Komputer);
Zestaw->Add(fourth);
cout<<"Cena "<<Zestaw->Name()<<" wynosi "<<Zestaw->ShowPrice()<<endl;
return 0;
Bibliografia:
Gamma E.,Helm R.,Johnson R., Vlissides J.:
Design Patterns: Elements of Reusable Object- Oriented Software , Addison-Wesley, 1995
Shalloway A.,Trott J.R.: Design Patterns Explained , Addison-Wesley, 2002
http://home.earthlink.net/~huston2/dp/patterns.html
http://www.openloop.com/softwareEngineering/patterns /designPattern/dPattern_wholePart.htm
Karel Driesen: Design Pattern (7) Composite, Visitor , Computer Science, Winter 2003