Objektově orientovaný návrh (v C++)

Lukáš

Objektově orientovaný návrh (v C++)
« kdy: 14. 03. 2012, 11:43:54 »
Dobrý den,

rád bych se zeptal na Váš názor na objektově orientovaný návrh řešení mého problému. Jde mi o to že v OOP se sice nějakou dobu pohybuju, ale tento konkrétní problém se mi nedaři nějak smysluplně vyřešit.

Jde o následující problém:

   - Mám jisté elementy (objekty) a zprávy, které těmto elementům posílám.

   - Elementy jsou instance tříd, které jsou všechny odvozeny od jedné abstraktní "base" třídy a tuto třídu rozšiřují.

   - Zprávy jsou taktéž instance tříd, které jsou všechny odvozeny od jedné abstraktní třídy zpráv a tuto třídu rozšiřují.

   - Každá třída zpráv (mimo kořenové abstraktní třídy) má v sobě public abstraktní třídu "rozhraní" a pokud chci, aby elementu došla určitá konkrétní zpráva, musí element dědit a tím pádem implementovat toto rozhraní (v opačném případě, kdy pošlu zprávu tomuto elementu a třída elementu by toto rozhraní neimplementovala, se nic nestane).

   - Požadavek je na to, aby bylo jednoduché přidávat nové zprávy i elementy (což by dosud nebyl problém, prostě se vytvoří nová třída, která bude dědit od předchozích...).

Problém je ale v tom, že je nutné zajistit určité chování zpráv - například vytvořím zprávu A, pošlu ji elementu X, a teď na základě nějakých podmínek (například, že jsem předchozí zprávu A poslal stejnému elementu) je nutné poslat zprávu B také tomuto elementu. Tento problém jsem řešil tak, že jsem každé zprávě přidal metodu pro "překlad" zprávy, která řešila tyto souvislosti. Pokud ale přidám nové třídy zprávy, je pravděpodobné, že budu muset upravovat tyto metody "překlad" již existujících tříd zpráv, což mi příjde určitě špatně a stejně tak se mi nezdá dobrá závislost tříd zpráv na sobě v tomto případě.

Napadlo mě vytvořit nějaký "controller" který by řešil tyto souvislosti, to mi ale také nepříjde v pořádku, jelikož by všechno zasílání zpráv bylo závislé na nějakém objektu controller, který by se s novými zprávami musel upravovat / měnit a tak podobně.
 
Pokud jsem to alespoň trochu objasnil, rád bych se tedy zeptal na Váš názor na tento návrh a řešení těch závislostí zmíněných výše a případně co je dle Vás špatně...

Děkuji

Lukáš
« Poslední změna: 14. 03. 2012, 13:03:16 od Petr Krčmář »


Waseihou

Re:Objektově orientovaný návrh (v C++)
« Odpověď #1 kdy: 14. 03. 2012, 14:12:50 »
Což to udělat takto:

Bude objekt který bude obsahovat mapu ukazatelů na funkční objekty zpráv které budou mít společného předka a implementovat nějakou funkci která bude prováděna nad tím objektem. Potom každému objektu se určí která rozhraní implementuje tím, že se mu dynamicky přidají tyto funkční objekty.

class Message;

class Object {
protected:
  std::map<int, Message*> _messages;
  int _X;
public:
   Object():_X(0){}

  void addMessage(int msgid, Message* msg) {
     _messages[msgid] = msg;
  }

  void sendMessage(int msgid) {
     std::map<int, Message*>::iterator it = _messages.find(msgid);
     if (it != _messages.end()) {
       (*(it->second))(this);
     }
  }

  int& X() {   return _X; }
};

class Message {
public:
  virtual void operator()(Object *obj) = 0;
};

class MyMessage : public Message {
public:
   virtual void operator()(Object *obj) {
       // funkcionalita sem
       obj->X() = 256;
   }
   
   static const int ID;
}

int MyMessage::ID = 0x01;

int main() {

 Object obj;
  MyMessage msg;
  obj.addMessage(MyMessage::ID, &msg);

  obj.sendMessage(MyMessage::ID); // vola

 Object obj2;

  obj2.sendMessage(MyMessage::ID); // nic se nestane

 return 0;
}

Je to jen draft, asi tam budou chyby. A nejsem expert na OPP, určitě to jde vylepšit.

Waseihou

Re:Objektově orientovaný návrh (v C++)
« Odpověď #2 kdy: 14. 03. 2012, 14:20:47 »
Jo všechny objekty které mají veřejný destruktor by ho měly mít virtuální, zvlášť pokud nechceš přemýšlet nad tím kdy to není třeba řešit, takže to taky narvi do bázové třídy.

Waseihou

Re:Objektově orientovaný návrh (v C++)
« Odpověď #3 kdy: 14. 03. 2012, 14:28:04 »
Mapa je jen pro jednoduchost ukázky, jinak udělej vektor a čísluj message id od 0 s tím že na začátku naplníš vektor speciálním objektem který nic nedělá pro pozice ID na které se nemá reagovat, pak bude mít volání konstantní složitost. Datovou strukturu msgid -> funkční objekt neřeším.

Do Object narvi rozhraní pro atributy a společné funkce pro všechny objekty a v potomcích je definuj, z funkčních objektů volej rozhraní a nebo atributy.

Sten

Re:Objektově orientovaný návrh (v C++)
« Odpověď #4 kdy: 14. 03. 2012, 18:18:13 »
Řešil bych to buď přes controller (resp. správná terminologie by byla dispatcher), nebo pomocí signálů.

Dispatcher by se měnit nemusel, pokud by byl šablonovaný. Jako literaturu doporučuji seriál Ondry Nováka.

Signály to řeší opačně. Objektům se nezasílají zprávy, ale objekty reagují na události. To umí třeba Boost (tím pádem odpadne starost se psaním :) ), jednoduchou implementaci lze nalézt třeba u Petra Pilaře.


Lukáš

Re:Objektově orientovaný návrh (v C++)
« Odpověď #5 kdy: 15. 03. 2012, 22:21:40 »
Moc Vám děkuji za rady, díval jsem se na ten problém pořád stejným způsobem a Vaše návrhy mi daly nový pohled na věc.  :)

Waseihou

Re:Objektově orientovaný návrh (v C++)
« Odpověď #6 kdy: 16. 03. 2012, 11:30:46 »
Nepřekomplikuj to, pokud to co to bude handlovat nebude obr.