250 likes | 419 Views
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
E N D
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
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
Kiedy używać Composite pattern ? Kiedy chcemy przedstawić obiekty w hierarchii part-whole, czyli w postaci drzewa Kiedy chcemy, żeby użytkownik traktował jednakowo zarówno jeden obiekt, jak również kontener zawierający kilka obiektów
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 Linepublic: 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(Graphicg){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 • The Composite Design Pattern by Nick Burns
...i to by było tyle na dzisiaj!