Brächte Rat bei Klassendesign: Abstrakte Basisklasse etc..



  • Hallo,

    im Moment sieht mein code vereinfacht und pseudo so aus:

    class Basis
    {
         //ein paar funktionen hier die aber nichts
         //zu meiner frage beitragen
    
         protected:
             virtual void foo() = 0;
    };
    
    class C1 : Basis
    {
          public:
    
          protected:
              virtual void foo();
    }
    
    class C2 : Basis
    {
          protected: 
            virtual void foo();
    }
    

    Jetzt sollen abhängig von einem user-parameter der grob gesagt die werte 0 - 3 haben kann von der Klasse C2 in der Methode foo() unterschiedliche Dinge passsieren. Z.B Statt der funktion a() die foo() aufgerufen wird, jetzt eine funktion b() aufrufen:

    C2::foo()
    {
          //....
    
         a()
    
         //..
    }
    

    Thematisch gehörte das aber alles in die Klasse C2 weil die sozusagen wieder so eine art Basisklasse für die 4 verschiedenen parameter.

    Ich möchte nicht für 4 parameter ständig if-elsif-elsif-else zweige verwenden und dacht evtl. daran 4 weiter Klassen zu bauen die alle ein spezielles foo() implementieren. Allerdings habe ich dann code 4-fach, bis halt auf ein paar ausnahmen. Wie macht man denn sowas? Sollte man da evlt. die Klasse C2 wieder als Basis für 4 weitere Klassen halten?

    Über ANregungen würde ich mich freuen - und ich kann keinen code hier zeigen weil es sonst den rahmen sprengen würde. Also bitte nicht mit solchen antworten. Danke 😉



  • Weiß nicht genau was du willst ... also natürlich kannst du bar(c2) als base nehmen ...

    class base
    {
    protected:
        virtual void call() = 0;
    };
    
    class foo : base
    {
    protected:
        void call();
    };
    
    class bar_base : base
    {
    protected:
        virtual void call();
    };
    
    class bar : bar_base
    {
    protected:
        void call();
    };
    

    ... oder:

    class base
    {
    protected:
        virtual void call() = 0;
    };
    
    class foo : base
    {
    protected:
        void call();
    };
    
    class bar : base
    {
        typedef void (base::*fnPtr)() const;
        enum type 
        { a, b, c, d};
        const type m_type;
    
    protected:
        std::map<type, fnPtr> m_calltype;
    
    public: 
        bar(type const& call) : m_type(call) {}
    
    protected:
        void call() { (*this.*m_calltype[m_type])(); }
    };
    
    class bar_derived : bar
    {
    public:
        bar_derived() : bar(a) { m_calltype.insert(std::make_pair(type::a, bar_derived::foo)); }
    
    public:
        void foo() const { std::cout << "bar_derived!" << std::endl; }
    };
    

    😛



  • Könntest Du vielleicht ein paar Worte verlieren was da im zweiten fall passiert? Function-Pointer? 😞



  • Ach, der schreibt immer so verwirrenden Code 🤡

    Wenn der Code so ähnlich ist, mach doch einfach eine switch-Unterscheidung. Deshalb solltest du keine 4 neuen Klassen entwerfen, das Konzept von OOP ist das ja nicht gerade.. Ein Kindklasse soll ja zur Elternklasse immer eine Ist-Ein-Beziehung haben und sie sollte vor allem _nötig_ sein 😉



  • Ok - danke....aber:

    [quote]mach doch einfach eine switch-Unterscheidung[quote]

    wie meinst du das? Meinst du evtl. ich sollte 4mal die methode schreiben mit jeweils der kleinen änderungen aber halt davor in die richtige mit switch verweisen?

    Danke



  • Wie der ist doch nicht verwirrend 😃

    Function-Pointer? 😞

    Nee... das wäre doch zu einfach 😛 Bzw. schon ... aber keine einfachen sondern sog. Method-Pointer.

    class bar : base
    {
        typedef void (base::*fnPtr)() const; // So sollen unsere Funktionen aussehen (void #name () const) ...
        enum type
        { a, b, c, d}; // diese Funktionstypen soll es geben ... kannst auch einfach einen Integer odg. nehmen. Ist nur damit du die Funktion eindeutig auswählen kannst.
        const type m_type; // const und private damit uns keiner mehr dran rumfummelt ... unser aktuelle Funktions-ID
    
    protected:
        std::map<type, fnPtr> m_calltype; // Map in der alle Method-Pointer einer ID zugewiesen sind.
    
    public:
        bar(type const& call) : m_type(call) {} // Im Kontruktor die Method-Pointer-ID festlegen ...
    
    protected:
        void call() { (*this.*m_calltype[m_type])(); } // Aufruf des Method-Pointer, der der aktuellen ID(m_type) zugewiesen ist. 
        // Das ist allerdings nur Pseudocode. Normal musst du hingehen und std::map<...>::iterator it = m_calltype.find(m_type); if (it != m_calltype.end()) (*this.*(it->second))(); nehmen ... 
        // also erst gucken ob überhaupt ein Method-Pointer für diese ID registriert ist ... 
        // wenn ja, soll der Method-Pointer mit dem this-Zeiger aufgerufen werden.
    };
    

    ... das zur Erklärung ...



  • puh.....danke.....da fängt man an zu glauben man könnte ein klitzeklein wenig programmieren und dann sowas 🙂



  • Allerdings habe ich dann code 4-fach, bis halt auf ein paar ausnahmen. Wie macht man denn sowas?

    Denn Teil vom Code der gleich ist ein Unterfunktionen der Basisklasse aufteilen.



  • sehe ich das richtig, dass ich mit den method-pointern einfach nur eine instanz von bar erzeuge und mittels des übergebenen arguments automatisch in dir richtige methode von den derived-klassen springe? oder doch nicht ? 😕



  • Denn Teil vom Code der gleich ist ein Unterfunktionen der Basisklasse aufteilen.

    ja das habe ich schon, nur trotzdem sieht es dann so aus:

    class derived : basis
    {
    
    protected:
            virtual void foo()
           {
                 //basis-funktionen
                 basefunc1();
                 basefunc2();
                 basefunc3();
    
                 //hier jetzt eben anhand eines parameters:
                if(param ==1)
                     //und noch weiteres
                     fkta();
                elsif (param == 2)
                     //und noch ein paar andere kleinigkeiten
                     fktb();
                else
                     fktc();
    
                 //basis-fuktionen
                basefunc4();
                basefunc5();
                 ....
           }
    
    };
    

Anmelden zum Antworten