Welches Entwurfsmuster? bedingte Erzeugung



  • Hallo,

    folgende Klassen:

    struct Q {};
    struct A: Q {};
    struct B: Q {};
    

    und folgende Funktion:

    Q f(int x)
    {
      if (x == 1) return A();
      if (x == 2) return B();
    }
    

    Im Klartext soll in Abhängigkeit von einem Parameter eine Instanz einer bestimmten Subklasse zurückgeliefert werden. Die Funktion f ist aber offensichtlich ziemlich unschön, da bei z.B. bei neu hinzukommenden Klassen diese Funktion immer geändert werden muss.
    Welches ausgefallene Entwurfsmuster löst dieses Problem?

    Danke!



  • mortified_penguin schrieb:

    offensichtlich ziemlich unschön, da bei z.B. bei neu hinzukommenden Klassen diese Funktion immer geändert werden muss.

    Factory-Pattern vielleicht.

    Das ist ein inhaltliches Problem, fürchte ich.

    gcc warnt z.B., wenn ein switsch nicht alle Fälle abdeckt. Damit kann man sicherstellen, daß man eine Warnung kriegt, wenn die erzeugende Funktion f nicht alle Fälle abdeckt.

    f sollte vermutlich Zeiger auf Q zurückgeben, A könnte ja mehr Speicher als B brauchen.



  • mortified_penguin schrieb:

    Hallo,

    folgende Klassen:

    struct Q {};
    struct A: Q {};
    struct B: Q {};
    

    und folgende Funktion:

    Q f(int x)
    {
      if (x == 1) return A();
      if (x == 2) return B();
    }
    

    Im Klartext soll in Abhängigkeit von einem Parameter eine Instanz einer bestimmten Subklasse zurückgeliefert werden. Die Funktion f ist aber offensichtlich ziemlich unschön, da bei z.B. bei neu hinzukommenden Klassen diese Funktion immer geändert werden muss.
    Welches ausgefallene Entwurfsmuster löst dieses Problem?

    Danke!

    wobei, damit das funktioniert, musst du mit zeigern (gerne auch mit smart pointers) arbeiten.
    ansonsten wird dein objekt a oder b auf q zusammengestutzt (slicing).
    Also: Q* f(int x){...}



  • Wieso muss abhängig von einem Parameter ein unterschiedlicher Typ instanziert werden; was genau ist das konkrete Problem, das du lösen möchtest?



  • Danke schonmal für die Antworten. Den Pointer hab ich beim zusammenstutzen vergessen.

    Konkret geht es darum dass ich über eine TCP/IP Verbindung PDUs bekomme. Es gibt verschiedene Arten von PDUs (erkennbar am ersten Byte). Diese "serialisierten" Daten will ich dann der entsprechenden Klasse als Konstruktorparameter übergeben, die sie dann in eine strukturierte Form aufbereitet.



  • mortified_penguin schrieb:

    Danke schonmal für die Antworten. Den Pointer hab ich beim zusammenstutzen vergessen.

    Konkret geht es darum dass ich über eine TCP/IP Verbindung PDUs bekomme. Es gibt verschiedene Arten von PDUs (erkennbar am ersten Byte). Diese "serialisierten" Daten will ich dann der entsprechenden Klasse als Konstruktorparameter übergeben, die sie dann in eine strukturierte Form aufbereitet.

    jede Klasse die du implementierst kann sich bei der Factory Funktion/Klasse ja mitsamt "ID" (in dem Fall eben das erste Byte) anmelden.
    Dadurch programmierst du die Factory Klasse genau einmal, und jeder neue PDU Typ, den du programmierst, registriert sich bei der Factory.
    Die Factory Klasse macht nichts anderes, als dass sie alle registrierten PDUs durchgeht und schaut, welcher Typ nun erzeugt werden soll (anhand dem ersten Byte).
    Dann ruft sie die registrierte CreatePDUx() Funktion auf, die dann tatsächlich ein passenden Objekt anlegt.

    Psudocode:

    //----------Modul 1: Interface für PDUs----------
    class IPDU
    {
     LoadDataFromStream(...);
    }
    
    //----------Modul 2: Factory----------
    // Klasse zum Erzeugen von PDU Typen - je nach ID (erstem Byte)
    // der Einfachheit halber jetzt mal als Singleton angelegt
    class Factory
    {
     Factory& Instance();
    
     bool RegisterPDUType(Byte firstByte,Callbackfunction createrFunc)
     {
      pdutypesMap.insert(firstByte,createrFunc);
      return true;
     }
    
     IPDU* CreatePDU(Datastream stream)
     {
      Byte firstByte=stream[0];
      Callbackfunction f=pdutypesMap[firstByte];
      return f();
     }
    };
    
    //----------Modul 3: ein konkreter PDU Typ----------
    // Konkrete Implementierung eines PDU Typs ("PDU1"), wird in dem Bsp. verwendet wenn erstes Byte=77
    // Interface für PDUs
    class PDU1: public IPDU
    {
     LoadDataFromStream(...);
    }
    
    IPDU* CreatePDU1()
    {
     return new PDU1();
    }
    
    // durch  die statische Variable wird erzwungen, dass die CreatePDU1 mitsamt der 
    // passenden ID bei der Factory registriert wird
    static bool register=Factory::Instance().RegisterPDUType(77,&CreatePDU1);
    

Anmelden zum Antworten