Wie am besten Basisstrukturen /-objekte verarbeiten? (Designfrage)



  • Hallo Leute,

    Ich habe eine Designfrage an euch. Ich nutze in meinem Code ein "Basisobjekt". Dieses beinhaltet Membervariablen für Typ, Länge und Rohdaten, auf die ich auch normal über getter/setter zugreife. Wenn ich so ein Objekt erhalte, dann muss ich es je nach festgelegten Typ unterschiedlich verarbeiten/parsen. Was ist der beste Weg? Mir fallen da 3 Wege ein:

    Erstmal das Basisobjekt:

    class Basisobjekt
    {
    public:
    	// diverse Konstruktor, Getter, Setter
    private:
    	int type;
    	std::vector<unsigned char> rohdaten;
    }
    

    Variante 1: Da die Parser keine Hilfsvariablen oder ähnliches nutzen, könnte man diese in ein namespace packen. Der Aufruf (in 'verarbeite()') ist allerdings etwas unübersichtlich, da ich als Parameter immer das Basisobjekt übergeben muss:

    namespace Parser
    {
    	namespace TypA
    	{
    		void machDies(Basisobjekt const& obj) {...}
    		void machDas(Basisobjekt const& obj) {...}
    		void machJenes(Basisobjekt const& obj, int irgendwas) {...}
    	}
    
    	namespace TypB
    	{
    		void tueDies(Basisobjekt const& obj) {...}
    		void tueDas(Basisobjekt const& obj, double irgendwas, int nochWas) {...}
    	}
    
    	...
    }
    
    void verarbeite(Basisobjekt & obj)
    {
    	if (obj.getType() == 1)
    	{
    		Parser::TypA::machDies(obj);
    		Parser::TypA::machJenes(obj, 123);
    	}
    	else if (obj.getType() == 2)
    	{
    		Parser::TypA::tueDies(obj);
    		Parser::TypB::tueDas(obj, 10.0, 678);
    	}
    	else if (...)
    	{
    	   ...
    	}
    	...
    }
    

    Variante 2: Ich erzeuge Klassen für die einzelnen Parser und übergebe bei der Instanziierung das Basisobjekt als Referenz. Somit spare ich mir gegenüber der 1. Variante den ersten Parameter, muss aber darauf achten, dass das Basisobjekt während der Verarbeitung gültig bleibt (naja, sollte immer der Fall sein). Da die Klasse sonst keine Membervariablen besitzt, weiß ich nicht ob diese Lösung optimal ist.

    class TypA
    {
    public:
    	TypA(Basisobjekt & obj) : bo(obj);
    
    	void machDies() {...}
    	void machDas() {...}
    	void machJenes(int irgendwas) {...}
    
    private:
    	Basisobjekt & bo;
    }
    
    class TypB
    {
    	...
    }
    
    void verarbeite(Basisobjekt & obj)
    {
    	if (obj.getType() == 1)
    	{
    		TypA t(obj);
    		t.machDies();
    		t.machJenes(123);
    	}
    	else if (obj.getType() == 2)
    	{
    		TypB t(obj);
    		t.tueDies();
    		t.tueDas(10.0, 678);
    	}
    	else if (...)
    	{
    	   ...
    	}
    	...
    }
    

    Variante 3: Irgendwas mit Vererbung. Man ahnt an dieser Aussage, dass ich Vererbung nicht so häufig nutze. Aber eventuell wäre hier die Lösung zu suchen.

    Wie würdet ihr sowas machen? Bzw. gibt es dafür ein spezielles Entwurfsmuster?
    Viele grüße,
    SBond



  • Guck dir mal das Visitor Pattern an.

    Andererseits:
    Rufst du für die verschiedenen Objekttypen unterschiedliche Funktionen mit unterschiedlichen Parametern auf? Das sieht dann nicht danach aus, dass Vererbung das Mittel der Wahl ist.



  • Ja, jeder Typ nutzt andere Funktionen und andere Parameter. Ich nutzte derzeit die Namespace-Variante mit 6 verschiedenen Typen. Mit diesen kann ich den std::vector im Basisobjekt je nach Art des Inhalts unterschiedlich verarbeiten. Mal repräsentiert der std::vector eine Liste von Werten, mal eine Fehlermeldung oder irgendwelche verschlüsselten Daten.

    ...ich schau mir das Pattern gleich mal an. Eventuell ist es ja das richtige.

    Edit: Also ich habe mir Visitor Entwurfsmuster angeschaut, habe allerdings nicht das Gefühl, dass es hier passen würde. Zumindest kriege ich dann kein wirklichen Ansatz hin.

    Wären aus meinem Beispiel Variante 1 und 2 ok oder ist das eine schlechte Umsetzung?


Anmelden zum Antworten