Frage bzgl getter-Methode



  • David_pb schrieb:

    Durchaus, aber das war nich die Anforderung die test-mensch stellte! 🙂

    Dann sollte sich test-mensch mit dieser Alternative lieber abfinden 😃

    Denn er manipuliert anscheinend von AUßEN ein privates Mitglied der Klasse ⚠ ⚠ ⚠

    Da macht es natürlich keine Sinn den Vector zurückzuliefern und ihn dann zu manipulieren...

    Und dem was Michael E. meint stimme ich auch zu, es macht keinen Sinn ( bzw. ist unsauber, nicht C++'isch 😉 ) ein Objekt über ne get-Methode zu manipulieren ...



  • Konrad Rudolph schrieb:

    Michael E. schrieb:

    Soll er ja auch gar nicht. Eine get-Methode gibt dir was zu lesen. Wenn du schreibenden Zugriff erlaubst, ist deine ganze Datenkapselung futsch.

    Wie begründest Du das?

    Ein Grundsatz der Datenkapselung ist, dass man keine gekapselten Datenfelder öffentlich macht. Der Sinn besteht darin, den Zugriff auf diese genau kontrollieren und, wenn nötig, konsistent abändern zu können. Nun braucht man aber auch oft Zugriff auf diese Member, deshalb gibts dann Getter zum (kontrollierten) Lesen und Setter zum (kontrollierten) Schreiben.

    [Edit] Diese Funktionen müssen und sollen vor allem bei komplexen Datentypen IMHO meistens nicht stupide den Parameter dem Member zuweisen bzw. den Member zurückliefern, sondern man sollte sich genau überlegen, welche Operationen unterstützt werden sollen, und für diese ein Interface geben. [/Edit]

    Wenn du jetzt über den Getter vollen Schreibzugriff implementierst, geht das erst mal völlig am Funktionsnamen und -zweck vorbei und zweitens hast du die Kontrolle über den Member verloren, denn der Benutzer der Klasse kann tun und lassen, was er will.

    Abgesehen finde ich diese ganze Hyper-Kapselung à la Java in C++ fehl am Platz, die bläht nur den Code auf und ist damit eher kontraproduktiv.

    Das sagst du bei diesem Beispiel?

    /EDIT: Der letzte Satz ergibt im aktuellen Kontext keinen Sinn.

    Begründung?



  • Michael E. schrieb:

    Konrad Rudolph schrieb:

    Michael E. schrieb:

    Soll er ja auch gar nicht. Eine get-Methode gibt dir was zu lesen. Wenn du schreibenden Zugriff erlaubst, ist deine ganze Datenkapselung futsch.

    Wie begründest Du das?

    Ein Grundsatz der Datenkapselung ist, dass man keine gekapselten Datenfelder öffentlich macht.

    Doch, man macht sie eben durch die Kapselung öffentlich -- allerdings (durch die Kapselung eben) kontrolliert. Es kann durchaus sinnvoll sein, über diese Kapselung auch Daten zu modifizieren, nicht nur zu lesen. Gerade bei Feldern ist das sinnvoll; eine eigene Methode zum Hinzufügen, Entfernen etc. von Daten zu schreiben ist redundant und IMHO falsch designed.

    Wenn du jetzt über den Getter vollen Schreibzugriff implementierst, geht das erst mal völlig am Funktionsnamen und -zweck vorbei

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen. Daher muss man wohl in den sauren Apfel beißen und sich für eine der Varianten entscheiden. Und das komplette Interface nachzuimplementieren halte ich für den falschen Ansatz.

    Abgesehen finde ich diese ganze Hyper-Kapselung à la Java in C++ fehl am Platz, die bläht nur den Code auf und ist damit eher kontraproduktiv.

    Das sagst du bei diesem Beispiel?

    Nein, sage ich eben nicht: Ich habe doch hinzugefügt, dass dieser Satz hier nicht passt. Ich hätte ihn auch entfernen können aber ich benutze Edits grundsätzlich nie zum Löschen in Postings.



  • Konrad Rudolph schrieb:

    Gerade bei Feldern ist das sinnvoll; eine eigene Methode zum Hinzufügen, Entfernen etc. von Daten zu schreiben ist redundant und IMHO falsch designed.

    Hmmmm, das leuchtet ein. Ist eigentlich logisch bei Vectoren und Konsorten, wenn man drüber nachdenkt ...
    Da hast du Recht 😉



  • ähm, also eure diskussion kann ich ja halbwegs nachvollziehen, dennoch bleiben fragen.

    wie schaut den nun eine einfach getter/setter methode aus? mal vorausgesetzt, ich wähle den meines erachtens nach auch besseren weg, dass ich sicherstelle, dass getter nur zum lesen gebraucht werden ( auch wenn sich das jetzt mit meiner ausgangssituation beißt 😉 ).

    ich möchte also eine const referenz zurückbekommen, richtig?

    wenn ich das nun so mache, wobei value ein einfacher int sei:

    const int &Test::getValue() const {
     return value;
    }
    

    bekomme ich ja eine referenz zurück.

    sollte ich nun einmal in die verlegenheit kommen, und mit dem rückgabewert weiterarbeiten wollen und ihn hierfür temporär in einer variablen speichern wollen, ist dann die referenz die ich zurückbekomme schon mein pointer (soweit ich weiß sind referenzen ja pointer die sich wie variable verhalten) oder muss ich dafür sorgen dass ich irgendwie

    Test t;
    int *tmp = t.getTest();
    

    ereiche?

    was bei mir nicht funktioniert ich aber eigentlich logsich fände, scließlich funktioniert ja auch

    int tmp = 5;
    int *ptmp = &tmp;
    

    nicht wahr? 😉

    zu später stunde bin ich schon ein bißchen malle in der birne, bitte seht mir also nach falls das alles nonsens ist 😃

    dankbar wär ich mal fpr eine einfache getter und setter methode mit meinetwegen einem einfachen int o.ä.

    soweit ihr h0x3r :p

    gut n8



  • Michael E. schrieb:

    Ein Grundsatz der Datenkapselung ...

    Das gilt aber nur solange es sich tatsächlich um Interna handelt. Und deine Behauptung

    Michael E. schrieb:

    Eine get-Methode gibt dir was zu lesen

    kommt irgendwie auch nicht ganz hin; eine get-Methode gibt dir was, Punkt.



  • Konrad Rudolph schrieb:

    Doch, man macht sie eben durch die Kapselung öffentlich -- allerdings (durch die Kapselung eben) kontrolliert. Es kann durchaus sinnvoll sein, über diese Kapselung auch Daten zu modifizieren, nicht nur zu lesen.

    wtf?

    Konrad Rudolph schrieb:

    Gerade bei Feldern ist das sinnvoll; eine eigene Methode zum Hinzufügen, Entfernen etc. von Daten zu schreiben ist redundant und IMHO falsch designed.

    class workshop
    {
    public:
      workshop( std::size_t max_participants, std::size_t min_age );
      // Kapselung "oeffentlich machen" aka "aushebeln":
      std::vector<person> get_participants();
    };
    
    int main()
    {
      workshop w(20, 16);
      vector<person> p = w.get_participants();
      generate_n(back_inserter(p), 1000000, generate_random_person);
    }
    

    Konrad Rudolph schrieb:

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen. Daher muss man wohl in den sauren Apfel beißen und sich für eine der Varianten entscheiden. Und das komplette Interface nachzuimplementieren halte ich für den falschen Ansatz.

    Äh, ja. Was ist denn der Unterschied zwischen Schreibzugriff und modifizierendem Zugriff?



  • test-mensch schrieb:

    sollte ich nun einmal in die verlegenheit kommen, und mit dem rückgabewert weiterarbeiten wollen und ihn hierfür temporär in einer variablen speichern wollen, ist dann die referenz die ich zurückbekomme schon mein pointer (soweit ich weiß sind referenzen ja pointer die sich wie variable verhalten) oder muss ich dafür sorgen dass ich irgendwie

    Test t;
    int *tmp = t.getTest();
    

    ereiche?

    Nein, die Referenz die du zurückbekommst ist schon deine Referenz, nicht dein Pointer:

    Test t;
    int& tmp = t.getTest();
    


  • finix schrieb:

    class workshop
    {
    public:
      workshop( std::size_t max_participants, std::size_t min_age );
      // Kapselung "oeffentlich machen" aka "aushebeln":
      std::vector<person> get_participants();
    };
    
    int main()
    {
      workshop w(20, 16);
      vector<person> p = w.get_participants();
      generate_n(back_inserter(p), 1000000, generate_random_person);
    }
    

    Was soll mir der Code sagen?

    Konrad Rudolph schrieb:

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen. Daher muss man wohl in den sauren Apfel beißen und sich für eine der Varianten entscheiden. Und das komplette Interface nachzuimplementieren halte ich für den falschen Ansatz.

    Äh, ja. Was ist denn der Unterschied zwischen Schreibzugriff und modifizierendem Zugriff?

    Schreibzugriff ist eine Zuweisung.

    vector<T> x;
    x = vector<T>(); // Schreibzugriff, sollte über 'set'-Methode gehen.
    x.push_back(T()); // Modifizierender Zugriff, sollte über 'get'-Methode gehen.
    


  • Hi,

    ich muß gestehen, dass ich erst überhaupt nicht Michael E.'s Meinung zur Datenkapselung teilte. Ich hatte schon ein "Fenster für den Widerspruch" geöffnet ... und nun denke ich doch, dass er (im Grunsatz) Recht hat:
    Wenn die kapselnde Klasse Test einen vector besitzt, muß sie auch die alleinige Verantwortung über seinen Inhalt haben.
    Das Problem bei dem nonconst-Ref-getter ist, dass sie aber keinerlei Möglichkeiten hat, diese Konsistenz zu gewährleisten.

    Gleichzeitig stimmt aber auch, dass ein "Durchreichen" der einzelnen vector-Methoden aufgebläht, fehleranfällig, verwirrend, .... einfach schlecht ist.

    Deswegen mein Vorschlag (gar nichts Exotisches): Klassischer getter und setter !

    Und genau DAS hat test-Mensch im Klassendesign richtig gemacht !

    Aber er hat eben nicht den Setter aufgerufen !

    Statt

    t.getVector().push_back(50);
    

    hätte der Aufruf lauten müssen:

    vector<int> v = t.getVector(); // gib mir eine Kopie Deines Vektors
    v.push_back(50); // Mache alle Veränderungen, die Du willst
    t.setVector(v); // Prüfe, ob das für Dich konsistent ist und setze das dann intern
    

    Eigentlich lautet die Antwort auf seine Frage

    warum die methode...nicht dem vector des objekts t den wert 50 zufügt

    "Weil Du t nicht mitgeteilt hast, dass Du seinen Zustand ändern möchtest !"
    😃

    Gruß,

    Simon2.

    P.S.: Jaja, ich weiß: Jeder C++er zuckt zusammen, wenn ein vector kopiert wird, aber hier geht es erstmal um ein sinnvolles Design - muss man anschließend (wirklich) optimieren (weil es der Test und das Profiling ergeben haben - nicht, weil man es "schöner" fände), kann man immer noch diverse Wege gehen.

    • z.B. tatsächlich einzelne "spezielle Zugriffsmethoden" durchschleifen ...
    • oder ein "Konsistentflag" mit einer entsprechenden "check()-Funktion" einbauen ...
    • oder intern eine Kopie zu halten, die man nach außen gibt und erst beim "sync()-Call" konsistenz zu checken und intern auf den "echten Vektor" kopieren
    • ....

    P.P.S.: Hat sich bei den Vorschlägen der anderen mal jemand überlegt, was passiert, wenn es in Test sinnvoller wäre, die Daten anders zu speichern als in einem vector ? Immerhin ist eine Idee hinter Kaspelung auch die Trennung der Schnittstelle von der internen Datenhaltung...



  • Ich weiß nicht, ob das Beispiel zu wild, oder extrem ungewöhnlich ist, aber was ist mit dem w3c-DOM?
    Was ist mit getElementsByTagName?
    Sollte es verboten sein, die erhaltenen Elemente zu modifizieren?

    Konrad Rudolph schrieb:

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen.

    Doch gerade in C++ kann man das sauber trennen. Es sind Java, C#, ..., die alle im Gegensatz zu C++ keine const correctness unterstützen.



  • schorsch code schrieb:

    ...

    Konrad Rudolph schrieb:

    Zu hast recht, Schreibzugriff sollte über einen Setter erfolgen -- modifizierender Zugriff aber IMHO nicht unbedingt. Leider kann man das in C++ AFAIK nicht (oder nur sehr aufwendig) trennen.

    Doch gerade in C++ kann man das sauber trennen. Es sind Java, C#, ..., die alle im Gegensatz zu C++ keine const correctness unterstützen.

    Ich habe nicht wirklich den Unterschied zwischen "Schreibzugriff" und "modizierenden Zugriff" verstanden ... aber (oder evtl. genau deswegen) mir fällt nicht ein, wie man in C++ per const dazwischen unterscheiden könnte.

    @Konrad: Meinst Du "Schreibzugriff" == "Zuweisung eines neuen Objektes"; "mod. Zuriff" = "verändern des internen Zustand des Objekts" ? ... 😕

    Gruß,

    Simon2.



  • Ich vermute, mit modifizierendem Zugriff ist indirekter Schreibzugriff gemeint. Das heißt du holst (Lesezugriff) dir eine Teilmenge des Objektzustands (wie z. Bsp. mit der erwähnten w3c-DOM-Methode getElementsByTagName). Dann erst modifizierst du (Schreibzugriff) das Objekt, indem du schreibend auf das Geholte zugreifst.



  • Konrad Rudolph schrieb:

    Was soll mir der Code sagen?

    War spät gestern. p sollte eine Referenz sein, get_participants eine Referenz zurückliefern. Als Beispiel für "öffentliche Datenkapselung", und gutes, nicht-redundantes Design sozusagen....

    Konrad Rudolph schrieb:

    Äh, ja. Was ist denn der Unterschied zwischen Schreibzugriff und modifizierendem Zugriff?

    Schreibzugriff ist eine Zuweisung.

    Und ein Schreibzugriff ist kein modifizierender Zugriff?



  • Hmmm... Interessante Diskussion, aber ich glaube ihr habt euch etwas verbissen...

    Habt ihr schon mal drüber nachgedacht, dass es nicht so viel Sinn macht, eine Klasse als kompletten Vector-Wrapper zu implementieren?

    Es ist sicherlich nicht dem Zweck entsprechend, einen Vector-Member zu haben und für jede der einzelnen Member-Funcs des Vectors einen Wrapper zu implementieren.
    Genauso zweifelhaft finde ich es, den Vector praktisch als public zu deklarieren.

    Wenn eine Klasse eine Reihe von Objekten verwalten soll, sollten eine Get, Add und Del-Methode doch reichen :xmas2:
    Und wenn nicht, sollte man evtl über eine andere Datenkapselung nachdenken 🤡



  • Badestrand schrieb:

    ...

    Hast Du zufällig meinen Beitrag gelesen ?

    Gruß,

    Simon2.



  • Sorry, aber irgendwie finde ich die Diskussion sinnlos. Ein Beispiel:

    class A
    {
      private:
        string name;
      public:
        string& getname();
        void setname(string);
    }
    

    Wie würdet ihr das hier lösen? string ist immerhin auch ein Container. Muß ich name jetzt auch nochmal kapseln?

    Meiner Meinung nach kommt es darauf an, in welchem Kontext der in diesem Thread diskutierte Vector steht. Welchen Zweck/aufgabe soll er in der Klasse erfüllen? Dementsprechende würde ich ihn komplett kapseln (add, get und delete Methoden) oder einfach oder per einfachem get bereitstellen.

    Übrigens, get-Methoden habe ich noch nie als "nur lesend" interpretiert. Get heißt für mich einfach nur, das ich etwas bekomme. Punkt. Wenn etwas "nur lesend" sein sollte, müsste man die Methode readX() benennen oder sowas.



  • Simon2 schrieb:

    Hast Du zufällig meinen Beitrag gelesen ?

    Öhh ja... Willst du damit sagen ich hätte genau das gleiche geschrieben?

    Grüße



  • Artchi schrieb:

    Sorry, aber irgendwie finde ich die Diskussion sinnlos. Ein Beispiel:

    class A
    {
      private:
        string name;
      public:
        string& getname();
        void setname(string);
    }
    

    Wie würdet ihr das hier lösen? string ist immerhin auch ein Container. Muß ich name jetzt auch nochmal kapseln?

    Kommt wohl darauf an, wie sich der Name nach außen präsentieren soll. Normalerweise behandelt man ja einen string als logische Einheit und dann reicht es aus, ihn als Ganzes rauszugeben.
    (übrigens solltest du darüber nachdenken, die getname() const zu setzen, sonst ist deine Datenkapselung wieder futsch*).

    * Ja, im einfachsten Fall reduziert sich die setname() auf ein einfaches "name=the_name;". Aber spätestens wenn du kontrollieren willst, was dort drin passiert, legst du dich gefährlich auf die Nase.



  • Normalerweise behandelt man ja einen string als logische Einheit und dann reicht es aus, ihn als Ganzes rauszugeben.

    Exakt! Und warum sollte das nicht auch bei einem vector der Fall sein?

    Man kann nicht pauschal sagen, der vector muß 100% gekapselt werden. Man muß erstmal den Kontext und die fachlichen Anforderungen berücksichtigen. Manchmal leitet man ja sogar vector private ab, und nutzt ihn nicht als einfaches Member. Weil ich halt den vector in dem speziellen Fall einem ganz anderen Kontext sehe.


Anmelden zum Antworten