Gesucht: Factory für die Messageverarbeitung aus Socket



  • Moin zusammen

    ich habe derzeit ein Konstrukt was ungefähr so aussieht:

    
    void onMessageReceived( const StandardMessage &msg )
    {
        switch ( type )
        {
            case 1 : 
            {
                Message1 m1;
                m1.parse( msg );
                handleMessage1( m1 );
                break;
            }
            case 2 : 
            {
                Message2 m2;
                m2.parse( msg );
                handleMessage2( m2 );
                break;
            }
        }
    }
        
    

    D.h. es kommt eine Message rein, die im grunde nicht mehr als Puffer ist. Unspezifisch also. Bekannt ist nur der MessageType.
    Daraufhin muss dieser Puffer gelesen werden und in eine spezifische Message umgewandelt werden. Die Beispiel-Messages hier sind die Klassen "Message1" und "Message2".

    Zu jeder dieser Message-Klassen gibt es eine eigene Funktion, in der die Message verarbeitet wird, nachdem sie geparst wurde.
    Alle Messages sind übrigens von einer Basisklasse abgeleitet.

    Nun würde ich gern diesen switch loswerden und die Sache etwas eleganter lösen. D.h. ich brauche folgendes:

    1. Anhand von MsgType das passende Message-Klassen-Objekt erzeugen
    2. parsen
    3. spezifische VerarbeitungsFunktion "handleMessage*" aufrufen. Die dann so aussieht: "handleMessage1( const Message1 &msg )"

    Ich stehe gerade etwas auf dem Schlauch und mir fällt nichts wirkllich gutes ein. Am Ende läuft alles auf die Arbeit mit der Basisklasse hinaus, die ich dann in der Verarbeitungsfunktion erst noch casten müsste, was ich vermeiden will.



  • Warum kann handleMessage1 nicht 1+2 selber erledingen?
    Dann einfach eine map mit Typ=>Funktion.



  • Aber das ginge dann eben nur unter verwendung der Basisklasse. In der Art hab ich das schon öfter gemacht, a la

    std::unordered_map<int, std::function...>
    

    Im Grunde gibt es ja auch zwei Teile, die spezifisch abgehandelt werden müssen.

    1. Die Erzeugung des KlassenObjekts anhand von Typ
    2. Der Call einer spezifischen Funktion für die Verarbeitung des Klassen-Objekts.


  • @It0101 sagte in Gesucht: Factory für die Messageverarbeitung aus Socket:

    Aber das ginge dann eben nur unter verwendung der Basisklasse.

    Nein, warum?



  • Achso. Jetzt weiß ich was du meinst. Einfach nur eine map auf eine Funktion, die als Parameter die "const StandardMessage &msg" übernimmt.

    Ja das ginge auch. Ich wollte aber den parse-Vorgang irgendwo zentral halten. Nur dass er jedesmal auf ein anderes Objekt aufgerufen wird.
    Aus einem einfachen Grund:

    Es wird nicht nur bei diesem parse()-Aufruf bleiben ( der ist in der Basisklasse sogar vergraben ). An der Stelle wird noch mehr Quellcode hinzukommen, daher wollte ich das nur einmal programmieren, aus Gründen der Fehlerminimierung. Bei am Ende ~30 Messages müsste ich den Parse-Kram 30 genau so oft programmieren.



  • template <typename Msg>
    Msg parse(const StandardMessage &msg)
    {
        Msg m;
        m.parse(msg);
        return m;
    }
    
    void handleMessage1(const StandardMessage &msg)
    {
       auto m = parse<Message1>(msg);
       ...
    }
    
    


  • Bekommt man dieses Konstrukt

    template <typename Msg>
    Msg parse(const StandardMessage &msg)
    

    Nur bekommt man dieses Konstrukt nicht als std::function in eine Map rein, oder täusche ich mich da?



  • @It0101 Nein, da steht handlemessage1 drin



  • Ja verstehe, worauf du hinaus willst. Das funzt natürlich. Ich wollte aber Quelltextvermehrung sparen. Und so habe ich quasi pro MsgType eine "handle"-Funktion und in jeder steht "parse". Naja ich tüftele mal noch ein bisschen, vielleicht krieg ich es noch besser hin. Danke dir erstmal!



  • @It0101 sagte in Gesucht: Factory für die Messageverarbeitung aus Socket:

    void onMessageReceived( const StandardMessage &msg )
    {
    switch ( type )
    {
    case 1 :

    Irgendwo im Ablauf setzt du die variable 'type'. Dort entscheidest du bereits, welcher MessageTyp verarbeitet werden soll. Weshalb gehst du den Umweg über einen numerischen Wert und wandelst diesen wieder in ein Objekt des gewünschten Typs?



  • @osdt sagte in Gesucht: Factory für die Messageverarbeitung aus Socket:

    @It0101 sagte in Gesucht: Factory für die Messageverarbeitung aus Socket:

    void onMessageReceived( const StandardMessage &msg )
    {
    switch ( type )
    {
    case 1 :

    Irgendwo im Ablauf setzt du die variable 'type'. Dort entscheidest du bereits, welcher MessageTyp verarbeitet werden soll. Weshalb gehst du den Umweg über einen numerischen Wert und wandelst diesen wieder in ein Objekt des gewünschten Typs?

    Der Quelltext ist nur symbolisch. Die Message besteht aus einem Header und der Nutzlast. Der Header wird direkt am Anfang geparst, weil für die Verarbeitung ja erstmal die MessageLaenge notwendig ist. In dem Header ist auch der "type" enthalten.



  • Du kannst dir auch das Visitor Pattern mal anschauen! Das sollte deine Problemstellung auch elegant lösen.



  • @pmqtt
    Ne, passt hier nicht.



  • Wenn man hier eine Factory nutzt, wird es keine Factory Methode sein können. Es müsste eine Factory Klasse sein, da man nur so in der Factory die Parser registriert bekommt. Diese Factory führt man sinnvoller Weise als Meyers Singleton aus, so dass die Parser sich in der statischen Initialisierung in ihr selbst registrieren. Damit das unabhängig von der Linkreihenfolge funktioniert, muss es ein Meyers Singleton sein. Die Parser werden ebenfalls als Singletons ausgeführt, und man legt jeweils eine statische Variable an, dadurch wird jeder Parser initialisiert und registriert sich selbst beim statischen Init des Programms.

    Verzichtet man auf Singletons, muss man das ganze von Hand beim Programmstart initialisieren.


Anmelden zum Antworten