Designfrage (Sichtbarkeit & Kapselung)



  • Hi Leute,
    Ich hab mal wieder eine Designfrage.
    Ich habe eine Klasse, welche sich um einen Verzeichnisbaum kümmert, mit Funktionen zum Hinzufügen und Löschen von Dateien und Verzeichnissen. Dann gibt es eine Klasse, welche eine private Membervariable von dieser Klasse hat.
    Diese zweite Klasse soll JEDE öffentliche Funktion der ersten Klasse auch anbieten, da der User ja nicht auf die Membervariable zugreifen kann, andererseits aber die volle Funktionalität benötigt. Naja, normalerweise würde ich jetzt einfach drauf scheissen und die Variable public machen, aber das Problem hierbei ist, dass die zweite Klasse eine Basisklasse ist und es sein kann, dass die abgeleiteten Klassen noch zusätzliche Überprüfungen drin haben müssen. Bisher hab ichs so: In der Basisklasse wird einfach nochmals jede Funktion der ersten Klasse implementiert. Die Funktionen rufen dann einfach die selben Funktionen der Membervariable auf.
    Zur Verdeutlichung ein kleines Beispiel:

    class A
    {
    public:
        bool AddFile(..){...}
        bool AddDir(..){...}
    }
    
    class B
    {
        A m_A;
    public:
        virtual bool AddFile(..){return m_A.AddFile(..);}
        virtual bool AddDir(..){return m_A.AddDir(..);}
        bool AndereMethode(..){..}
    }
    
    class B1 : public B
    {
    public:
        bool AddFile(..){if(Bed) return m_A.AddFile(..); else return false;}
    }
    

    So, meine Frage: Wie kann ich das anders lösen? Ich will nämlich eigentlich nicht jede Funktion von A in B nochmals definieren, solange sie nur direkt die Funktion von A aufruft ...

    Danke im Voraus.

    Edit: virtual reineditiert 🙂



  • öhm, wie wärs mit Vererbung? Ansonsten könntest du es mit dem Cast Operator versuchen.

    btw. ist in deinem Beispiel ein Fehler, du versuchst eine nicht virtuelle Funktion zu überschreiben tststs 🙂



  • doofie, das ist erstens kein wirklicher Fehler und zweitens war das doch nur für euch zur veranschaulichung 🙂
    Du meinst, dass B von A erbt? Was meinst du mit dem Cast-Operator?



  • kingruedi ist doch kein doofie 😃



  • edit: viel zu lange gebraucht...

    Ich bin mir da nicht so ganz sicher, was Du erreichen willst...

    Auf jeden Fall ist es wohl nicht so günstig in Klasse B1 eine nicht virtuelle Methode von B zu überschreiben. Das könnte für Verwirrung sorgen.

    Falls Du also überladen willst brauchst Du sowieso eine virtuelle Methode in der Basisklasse. Die mußt Du entweder selber schreiben, oder von A erben.
    Im Falle von selber schreiben könnte eine Default-Implementierung genauso aussehen, wie von Dir geschildert.

    Dann sollte A allerdings die Methoden als virtuell deklarieren. Das kommt dann aber auf A und B an. Mir scheint es hier nicht unbedingt zu passen, da ich aber die echten Namen und Kontexte der Klassen nicht kenne kann ich nur raten.

    Ansonsten fällt mir nur noch ein kleiner Hack ein:
    operator -> für Klasse B überladen, so daß sie einen Zeiger auf das Objekt A liefert.
    Nachteil:
    Die Syntax
    ObjektB.blabl();
    ObjektB->BenutzewasvonKlasseA(); ist nicht sehr intuitiv.
    Außerdem könnten wir das Objekt dann auch gleich wieder public machen, wenn wir schon nen Zeiger drauf rausgeben.



  • Wie wäre es mit privater (oder protecteder) Ableitung und using-Deklarationen?

    Edit: OK, wenn die Methoden in B virtual sein sollen, ist das natürlich unpraktisch. Kannst du nicht einfach alle Methoden von A virtual machen und public ableiten? Angst vor Performance oder "ist B kein A"?



  • Jester: Das mit dem Zeiger is Quark, wie du sagst: dann könnte man es gleich public machen.

    operator void: Kannst du das mal etwas genauer erklären?



  • Vor deinem virtual hätte ich es so umgesetzt (sollte so doch gehen?):

    class A
    {
    public:
        bool AddFile(..){...}
        bool AddDir(..){...}
    }
    
    class B : private A
    {
    public:
        using A::AddFile; // Spart immerhin Tipparbeit.
        using A::AddDir;
        bool AndereMethode(..){..}
    }
    
    class B1 : public B
    {
    public:
        // Hier wird's dann aber unschön.
    }
    


  • auf deutsch: so lassen, wie ichs hatte?



  • dEUs, ich denke nicht daß Du es schaffst um die zusätzlichen Funktionen effektiv herumzukommen - Du schreibst ja selbst, daß Ableitungen zusätzlichen Code benötigen - dieser zusätzliche Code muß plaziert werden - also hast Du eine Funktion, einen Wächter, und dann den Aufruf von m_A.

    Deine Idee mit dem public ist übrigens furchtbar und kann teilweise verbessert werden, wenn Du wenigstens sowas machst:

    class B
    {
    private:
       A m_A;
    public:
       const A& getA() {return m_A;}
    ...
    };
    

    Dies erlaubt nämlich viele Möglichkeiten:
    - Du mußt nicht die ganzen Getter erneut implementieren
    - das Objekt ist wg. const davor geschützt, daß es ohne Wächter beschrieben wird
    - Du kannst im Zweifelsfalle getA sogar noch ändern und eine von A abgeleitete Funktion übergeben, sollte sowas nötig sein



  • Wenn ichs mit GetA mach, hab ich ja immer noch das Problem, dass direkt die Funktionen von A aufgerufen werden und B1 seine zusätzliche Kontrolle nicht machen kann ...



  • Nur die const(!)-Methoden... da brauchst Du im Regelfalle ja keinen guard.

    Ich liefere nur eine const& zurück.



  • Ahso ... Du meinst, dass ihc mit dieser Methode die Getterfunktionen von A nicht in B neu implementieren muss, die restlichen aber schon?



  • Ich dachte an sowas:

    class A
    {
    public:
       int getQ() const;
       int getR() const;
       void doSomething();
       void setQ(int q);
    ...
    };
    class B
    {
    private:
       A m_A;
    public:
       const A& getA() const {return m_A;}
    ...
    };
    
    B myB;
    myB.getA().getQ();
    myB.getA().getR();
    myB.getA().setQ(5); // VERBOTEN
    myB.getA().doSomething(); // VERBOTEN
    

    Du verrätst damit natürlich Details und der Aufrufer kommt an alle const-Methoden von A ohne weitere Kontrolle ran, aber... wie gesagt. Kann eine Lösung sein.



  • Ich verstehe nicht ganz, warum B (und damit B1 usw.) nicht von A erben kann. Du hast in deinem ersten Artikel des Threads ausdrücklich verlangt, daß B jede public-Methode von A ebenfalls besitzen soll. Voila! Genau das ist doch der Fall beim public Vererbung. Oder habe ich da was übersehen?

    Stefan.



  • Im Grunde hast du Recht. Aber irgendwie gefällt mir das net. B ist ja kein ExtendedDirectoryTree, wenn man so will, sondern hat eben nur diese Funktionaläität, zusätzlich zu nem ganzen Haufen anderer Funktionalität ...



  • dEUs schrieb:

    B ist ja kein ExtendedDirectoryTree, wenn man so will, sondern hat eben nur diese Funktionaläität, zusätzlich zu nem ganzen Haufen anderer Funktionalität ...

    Aber doch gerade diese Aussage belegt ja die Anwendung von public-Vererbung. Wenn B tatsächlich die gesamte Funktionalität von B besitzt plus zusätzliche Informationen und Funktionalität, dann ist dies sehr angebracht.



  • Na ok, überredet 😉


Anmelden zum Antworten