wie macht man es schön? fabrikpattern, oder geht es besser?
-
hallo zusammen,
ich will mal abstrakt mein problem beschreiben. ich habe die module A, B und C, welche unabhängig von einender sind. diese senden und empfangen alle nachrichten, welche eine gemeisame abstrakte basisklasse haben. nun hab ich ein weiteres modul D, welches die nachrichten von A, B und C alle serilisiert und wegspeichert. beim deserializieren bekomme ich den typ der nachricht, die größe und das byte array, welches die rohdaten sozusagen sind. allerdings um die nachricht wiederherzustellen, müßte ich erst eine konkrete instance anlegen, in welche ich die daten reinschreibe. dazu müßte aber D wissen, welche nachrichten es in A, B und C gibt. das ist aber dämlich. Eine zentrale Factory will ich eigentlich auch nicht, da die module unabhängig weiterentwickelt werden und man daher immer alles nachpflegen müßte. in java würde ich es über ne art reflection machen, ala (pseudocode):
ClassType getClassFromType(MessageType type); ... MessageType aType; Object message = getClassFromType(aType).createInstance(); message.desiralize(bytearray);Ich habe ein MessageType enum, welches für alle Nachrichten einen eindeutigen Typ definiert. Die Stelle könnte man um so eine getClassFromType Geschichte mit begrenzten Aufwand aufbohren, aber wie? Geht es noch besser, anders, mehr C++ mäßiger???
Für Hinweise, Fragen, Bemerkungen, Tipss und so weiter bin ich dankbar.
-
uwerothfeld schrieb:
die größe und das byte array
Sicher, dass du nicht ein
std::vector<unsigned char>willst?Sonst verstehe ich im Moment nicht dein "Problem".´

Edit: Ich habe bisher verstanden: Du hast Modul A, B, C die von einer gemeinsamen Basisklasse ableiten; es sind Module für die Nachrichtenverwaltung o. ä.
D stellt eine Serialisierung für A, B, C da (kennst du schonboost::serialization?). Irgendwie hast du ein Problem mit der Serialisierung, weil ... und hier komm' ich nicht weiter.
-
Hallo Sone,
nein, du hast mich leider falsch verstanden :). Nochmal. Ich habe drei Module, A, B, C, welche Dienste implementieren, lustige Nachrichten hin und her senden und empfangen.
Alle Nachrichten, welche versendet werden, leiten von einer gemeinsamen Basisklasse ab.
Hier im Projekt gibt es leider eine bescheidene eigene Serilisation, welche A, B, C verwenden.
Modul D soll nun alle Nachrichten von A, B, C speichern. Es bekommt eine Referenz auf die Basisklasse der Nachrichten, ruft die Serilisation auf und erhält ein ByteArray. Das ByteArray ist, wie du schon vermutet hast, wie folgt definiert:
typedef unsigned char byte; class ByteArray : public std::vector<byte> { }Das speichern der Nachrichten funktioniert auf diesen Weg gut. Nun will Modul D allerdings die Nachrichten wieder auslesen. Die Frage ist nun, wie kann ich die Deserilisation anwerfen? Weil dazu benötige ich eine quasi leere Instance der spez. Nachricht (Subklasse der Basisnachricht), welche ich mit den Daten "fülle". Ich will nun keinen riesen switch/case Block haben, in dem ich jeden Nachrichtentyp abfrage muß und sobald neue Nachrichten hinzukommen oder neue Module muß ich wieder alles anfassen. Daher mein Skizze der Java Reflection Idee. Hat aber wohl mehr verwirrt.
War dies klarer?
Vielen Dank.
-
Naja, in Java hast du damit ja quasi eine zentrale Factory. Wenn du eine zentrale Verwaltungsstelle haben willst, wirst du dir eine bauen müssen, darum kommst du nicht herum.
Intern sollte die aber kein Gott-Objekt sein; ich stelle mir etwas wie eine std::map<nachrichtentyp, funktionszeiger> vor, in dem die jeweiligen Module ihre eigene Factory-Funktion registrieren können, wenn sie geladen werden. Wenn du von den Klassen, um die es geht, einen Default-Konstruktor erwarten kannst, habe ich da etwas entsprechendes herumliegen:
#ifndef INCLUDED_FACTORY_HH #define INCLUDED_FACTORY_HH #include <map> #include <sstream> #include <stdexcept> #include <tr1/memory> namespace impl { template<typename base_t> struct basic_instantiator { virtual base_t *operator()() const = 0; virtual ~basic_instantiator() { } }; template<typename base_t, typename class_t> struct instantiator : basic_instantiator<base_t> { virtual base_t *operator()() const { return new class_t(); } }; } template<typename key_t, typename base_t> class factory { public: typedef key_t key_type; typedef base_t base_type; typedef std::map<key_type, std::tr1::shared_ptr<impl::basic_instantiator<base_type> > > type_map; template<typename class_t> void register_type(key_type const &key) { types_[key].reset(new impl::instantiator<base_type, class_t>()); } base_t *operator()(key_t const &key) const { typename type_map::const_iterator i = types_.find(key); if(i == types_.end()) { throw std::invalid_argument("type unregistered in factory"); } return (*i->second)(); } private: type_map types_; }; #endifDann muss halt irgendwo, wo die konkrete Klasse bekannt ist, diese der Factory bekannt gemacht werden:
factory<std::string, A>; fct.register_type<B>("foo"); std::auto_ptr<A> ptr(fct("foo"));In deinem Fall mag es allerdings sinnvoller sein, eine unordered_map als Container heranzuziehen.
Auch einen Blick wert: http://www.boost.org/doc/libs/1_50_0/libs/functional/factory/doc/html/index.html
-
Das war schonmal klarer, jetzt verstehe ich dein Problem.

Allerdings wirste dich um eine "spezielle Behandlung" nicht herumschwänzeln können.//EDIT: Natürlich eine Funktion. std::unique_ptr<BasisNachrichtenKlasse> getNachrichtenKlasseFromMessageType(MessageType a) { switch(a) { case Bla: return std::unique_ptr<BasisNachrichtenKlasse>(new BlaNachrichtenKlasse(bytearray)); //... } }Das wär mein Ansatz (nimm' aber lieber Seldons... XD ).
-
An alle: Vielen Dank für die Hilfe. Der Ansatz seldon ist schön, und so habe ich es auch umgesetzt. Hat ein wenig gebraucht, bis es lief, aber nun ist schön.
Vielen Danke!