Klassen ohne getter/setter?



  • Weil es Inkonsistenz zur Folge haben könnte.



  • Apollon schrieb:

    Badestrand schrieb:

    Und komplett ohne Getter? Weil einige Daten muss man ja rausholen?

    Warum muss man das? Warum nicht einfach das Objekt, das die noetigen Daten bereithaelt, uebergeben?

    Ach stimmt, irgendwie meinte ich das auch gar nicht so 😕 Meine Gedanken waren eben wie folgt: Die Methode "size()" in std::string sei ja auch ein Getter, weil es die Länge zurückgibt.



  • und wie kommt man an die nötigen Daten rann ?



  • Knuddlbaer schrieb:

    und wie kommt man an die nötigen Daten rann ?

    Jedenfalls nicht, indem man eine Referenz auf ein Klassenobjekt zurückgibt. Eher halt die nur wirklich nötigen Daten als Datensatz rausgeben. Mit Referenz ist die Klasse z.B. gezwungen, selbst ein Objekt dieser Art zu halten. Bei der Methode "size" von std::string können die Daten ja auch berechnet werden, es ist halt entkoppelter. So sehe ich das jedenfalls.



  • Knuddlbaer schrieb:

    und wie kommt man an die nötigen Daten rann ?

    Hoe? Man hat doch freien Zugriff drauf... Ach! Du meinst von aussen?! Nochmal: man reicht nicht Daten rum, sondern Objekte.



  • Natürlich kann man einen Öltanker auch selbst mit dem Zollstock abmessen. Aber wäre es nicht wesentlich einfacher, der Konstrukteur sagt mir einfach, wie lang das Schiff ist?



  • Apollon schrieb:

    Knuddlbaer schrieb:

    und wie kommt man an die nötigen Daten rann ?

    Hoe? Man hat doch freien Zugriff drauf... Ach! Du meinst von aussen?! Nochmal: man reicht nicht Daten rum, sondern Objekte.

    Davon das Du Deine Aussage wiederholst, wird sie nicht besser. Was fange ich mit einem Objekt an, das ich übergeben bekommen habe ? Was bringt mir ein Objekt, das Berechnungen vornimmt wenn man die Daten nicht verwenden kann ?



  • Badestrand schrieb:

    Ich fände so einen Artikel auch interessant! Wie sollte er denn aufgemacht sein? Generelles Klassen-Design vor dem eigentlich Programmieren (also Planung) mit Fallbeispielen oder wie?
    Und komplett ohne Getter? Weil einige Daten muss man ja rausholen?

    Mir geht es nicht um allgemeines OOD. Mir geht es wirklich um diese Getter/Setter-Sache. Ich pers. kann es mir halt nicht vorstellen. Es gibt natürlich hier und da Situationen, wo man ohne diese auskommt. Aber das klassische Beispiel ist z.B. das mit dem Modell. Oder ein anderes Beispiel:

    class Widget
    {
       public:
          void setPosition(Point);
          Point getPosition();
    };
    

    Sorry, aber wenn etwas eine Eigenschaft hat (z.B. eine 2D-Koordinate), wie soll ich das anders machen?
    Und size() von vector ist auch nur ein getter. Genauso wie resize() auch ein setter ist. Fehlt nur das Prefix set oder get bzw. sie heißen anders. Aber der Effekt ist der gleiche.

    Oberes Beispiel würde wohl so werden:

    class Widget
    {
       public:
          void move(Point);   // das gleiche wie setPosition
          Point position();   // das gleiche wie getPosition
    };
    

    Ist aber am Ende auch nur getter/setter.

    Also, mich würden solche realen Szenarios interessieren, wenn ich eine Entität habe (wie das Model im MVC-Pattern!). Weil normalerweise bekommt man nur zurecht geschnittene Szenarios präsentiert. Aber es gibt soviele GUI-Libs die alle mit getter/setter arbeiten... ich wills jetzt endlich mal konkret wissen, wie man das anders lösen könnte.



  • Du machst den Koordinatenpunkt public. Da die Semantik von set es sowieso nicht zuläßt, Konsistenzprüfungen vorzunehmen, brauchst Du natürlich auch keinen Setter mehr. Und für eine public Koordinate brauchst Du natürlich auch keinen Getter mehr.



  • scrub schrieb:

    Du machst den Koordinatenpunkt public. Da die Semantik von set es sowieso nicht zuläßt, Konsistenzprüfungen vorzunehmen, brauchst Du natürlich auch keinen Setter mehr. Und für eine public Koordinate brauchst Du natürlich auch keinen Getter mehr.

    In dem GUI-Fall musst du aber auf die Positions-Veränderung reagieren, indem du dein Widget verschiebst. So einfach ist es dann doch nicht.



  • Naja, ich vertrete die Meinung auch nicht selbst, sondern das ist das, was ich davon verstehe. Ich finde es auch nicht sinnvoll. Insofern habe ich keine Ahnung, wie das Problem in einer "in dem GUI-Fall" damit anzugehen wäre.



  • Knuddlbaer schrieb:

    Apollon schrieb:

    Knuddlbaer schrieb:

    und wie kommt man an die nötigen Daten rann ?

    Hoe? Man hat doch freien Zugriff drauf... Ach! Du meinst von aussen?! Nochmal: man reicht nicht Daten rum, sondern Objekte.

    Davon das Du Deine Aussage wiederholst, wird sie nicht besser. Was fange ich mit einem Objekt an, das ich übergeben bekommen habe ? Was bringt mir ein Objekt, das Berechnungen vornimmt wenn man die Daten nicht verwenden kann ?

    Beispiel MVC-Pattern: ich muß doch wissen, woher ich die Daten des Models bekomme, wenn ich das View bin.

    Eine andere Möglichkeit wäre, das ich beim Update gleich die Daten bekomme.

    class Widget
    {
         public:
              void update(Model);
    };
    

    Wäre eine Möglichkeit. Aber wie soll sich die View (Widget) beim Model abmelden, wenn es das Model nicht kennt? Man könnte es extern lösen, aber wäre es nicht einfacher, wenn sich das Widget selber im Dtor vom Model abmeldet? Dazu muß ich aber ein Widget::setModel(Model) haben...

    class Widget
    {
         Model *model;
         public:
              void setModel(Model *m)
              {
                   model = m;
              }
              ~Widget()
              {
                   model->remove(this); // beim Model abmelden
              }
    };
    

    Ihr merkt, hier ist einfach Praxismangel vorhanden, wenn man ohne Getter/Setter auskommen soll.



  • scrub schrieb:

    Du machst den Koordinatenpunkt public. Da die Semantik von set es sowieso nicht zuläßt, Konsistenzprüfungen vorzunehmen, brauchst Du natürlich auch keinen Setter mehr. Und für eine public Koordinate brauchst Du natürlich auch keinen Getter mehr.

    Public-Variablen widerspricht aber der OO-Philosophie! Und außerdem will ich sehr wohl eine Prüfung beim setPosition (oder move) durchführen, z.B. keine negativen Koordinaten erlauben.



  • Mir wurde und wird aber entgegnet, dies sei durch "die Semantik" von Set verboten. Setter dürfen nicht auf Konsistenz prüfen, heißt es.
    Das ist wirklich kein Witz und keine Erfindung von mir... das ist die Argumentation. Der Setter darf nur setzen, sonst nichts. Also kann man, wenn man einen Setter anbietet, die Variable auch gleich public machen.



  • Gewisse Getter brauchst du auf jeden Fall, sonst bringen dir deine Objekte oft wirklich nichts. Ein Datei-Objekt muss beim Lesen halt die Daten der Datei zurückgeben, anders gehts nicht. In bestimmten Fällen (vor allem bei GUI-Sachen) geht's wahrscheinlich auch oft nicht "besser".
    Oft kann man gerade Setter aber verhindern, indem ein Objekt bei der Konstruktion Daten mitbekommt (oder es sich diese erarbeitet) und diese nur noch abfragbar sind.

    Aber ich denke, man muss da sowieso noch mehr differenzieren. Eine Klasse, die nach dem Getter-/Setter-Prinzip designed ist, unterscheidet sich ja in der Konzeption schon sehr stark von einer Klasse, die darauf nicht ausgelegt ist.

    Beispiel (hoffentlich kein zurecht-geschnittenes Szenario): Wir haben eine Klasse, die eine Datei repräsentiert. Die Datei kann Integer speichern, umbenannt, verschoben, ausgelesen und beschrieben werden.
    Getter/Setter:

    class DateiManager
    {
        Datei getDatei( pfad )       // Gibt entsprechendes Datei-Objekt zurück
    }
    
    class Datei
    {
        void setContent( int i[] )
        int[] getContent()
    
        string getName()
        void setName( name ) // benennt Datei um
    
        string getPath()
        void setPath( path ) // verschiebt Datei
    }
    

    Anderes Design:

    class DateiManager
    {
       Datei getFile( pfad )                 // Gibt entsprechendes Datei-Objekt zurück
       void moveFile( src_path, dst_path )   // Verschiebt Datei
       void renameFile( src_path, new_name ) // Benennt Datei um, meinetwegen auch setFileName(...)
    }
    
    class Datei
    {
        void clear()                         // Leert Inhalt
        int read()                           // Liest einen Integer
        void write( int i )                  // Schreibt Integer
        int seek( int pos )                  // Bewegt den Dateizeiger
    
        // Zu beachten: Datei kennt weder ihren Pfad noch ihren Namen!
    }
    

    Das ist ganz sicher kein Ideal-Beispiel, aber ich denke, ein paar wesentliche Punkte sind drin: Objekt gibt nur nötigste Informationen raus (Datei gibt nur den Inhalt, nicht Namen oder Pfad), Verantwortung liegt so hoch wie möglich (Dateimanager ist für's Datei-verschieben zuständig, nicht die Datei) und differenziertere Methoden (clear, seek, write vs setContent).

    Artchi schrieb:

    Beispiel MVC-Pattern: ich muß doch wissen, woher ich die Daten des Models bekomme, wenn ich das View bin.

    Das einzig sinnvolle beim MVC finde ich, den Datenbestand als Singleton zu halten (was spräche dagegen?).

    class Widget
    {
       public:
          void setPosition(Point);
          Point getPosition();
    };
    

    ➡

    class Widget
    {
    }
    
    class WidgetParent/-Container/-Manager/-Holder
    {
        void addWidget( widget, position )
        void setWidgetPosition( widgetID, new_position )
    }
    

    Du kommst nicht drumherum, eine Methode zu haben um die Position des Widgets zu setzen, schließlich braucht man die Funktionalität. Wenn man sowieso die Funktionalität braucht (die zum Auslesen der Position braucht man z.B. nicht), ist es nur noch die Frage, in welcher Form und bei welcher Zuständigkeit man die Methode implementiert. Und diese beiden Sachen, zusätzlich zu der Entscheidung ob man eine Funktionalität braucht, sind es meiner Meinung nach, die dann das Getter/Setter-Design ausmachen oder auch nicht.

    PS: Ich gebe zu, das Beispiel mit der Datei ist etwas schwammig. Ich hab mich bisher auch noch nie groß mit der Getter/Setter-Frage beschäftigt, mir ist aber aufgefallen, dass ich in meinen Quelltexten quasi fast gar keine Setter/Getter-Kaskaden eingebaut hab und an der Arbeit baue ich auch ab und an Getter/Setter aus (und das Design verbessert sich imho dadurch).



  • Ich bin jetzt noch recht neu in C++.
    Nur irgendwie verstehe ich die Diskussion nicht ganz.
    Natürlich ist es so, das ich niemals einen getter schreibe, wenn ich von Anfang an weiss, das ich niemals mehr den Wert mehr auslesen brauche, sondern dieser nur für interne Berechnungen z.B. in der Klasse notwendig ist.
    Oder schreibt jemand einen Getter grundsätzlich für alle Member Variablen ?
    Aber sobald ich irgendwie auf meine private Member Variablen zugreifen will ob nun zum setzen oder zum lesen, ist es doch immer eine Setter bzw. Getter Methode, egal wie ich sie nenne.
    Wenn ich die private dann einfach in den public Part packe, dann brauche ich zwar keine Getter/Setter, aber das widerspricht doch dem Grundgedanken der OO.
    Und die Argumentation, das man anstelle des getters dann einfach das Objekt zurückgibt kann ich irgendwie auch nicht Ansatzweise nachvollziehen. Ich will ja nunmal an die privates des Objektes ran. Und das geht ja nur über eine getter.
    Bitte klärt mich mal jemand auf, ob ich da was komplett missverstanden habe.
    😕



  • Was reicht ihr weiter (in einem C+-Programm): einen string oder einen char-array? Einen vector oder die interne Repraesentation davon?

    Im Uebrigen stammt die Argumentation, mit der scrub konfrontiert wurde in erster Linie von mir.



  • Was reicht ihr weiter (in einem C+-Programm): einen string oder einen char-array? Einen vector oder die interne Repraesentation davon?

    Versteh den Vergleich nicht. Vector und String sind Typen, richtig. Aber diese Typen werden dadurch "besonders" das sie bestimmte Eigenschaften haben. Und diese Eigenschaften sind vielfältig: Einzelne Elemente, Anzahl Elemente, Reservierte Elemente usw. So, und wie komme ich an diese Eigenschaften ran? Über getter und setter (nur heißen die nicht so, Namen sind Schall und Rauch):

    string str("Hallo");
    char a = str.at(0);   // Getter auf ein Element 0, heißt nur nicht get()!
    str.at(0) = 'B';      // Setter auf ein Element 0!
    str.c_str();       // Getter auf alle Elemente
    size_t s = str.size();  // Getter auf Eigenschaft Länge, heißt nur nicht get_size()
    str.resize(100); // Setter auf Eigenschaft Länge, heißt nur nicht set_size()
    

    Natürlich übergebe ich str an eine Funktion, wenn sie einen string haben will. Aber das bestimme nicht ich, sondern die Funktion, die ich benutzen will.

    Und genau das gleich wie Widget: ich habe Widget, das reiche ich weiter, wenn jemand ein Widget haben will. Aber was ist wenn er die Caption oder die XY-Pos des Widgets haben will?

    Also wirklich schlau werde ich aus dem Konzept "keine Getter/Setter" nicht. Schön das ich String- oder Widget-Objekte weiter reiche. Macht man ja auch und spricht nichts dagegen. Aber gaaanz am Ende muß man ja auf die Eigenschaften eines Objektes zugreifen. Wie soll das ohne Getter/Setter gehen? Oder geht es am Ende doch nur um die Namesgebung einer Member-Funktion? 😃



  • Im Uebrigen stammt die Argumentation, mit der scrub konfrontiert wurde in erster Linie von mir.

    Dann bin ich jetzt mal auf die Argumentation gespannt, warum ein Setter keine Prüfungen vornehmen darf. Die Argumentation des Getters und deren Objekte ist ja schon recht verdreht (siehe Artchi mit der Ausführung der Getter beim String Objekt), so das die Argumentation für Setter sicherlich sehr interessant wird 🤡



  • Dann bin ich jetzt mal auf die Argumentation gespannt, warum ein Setter keine Prüfungen vornehmen darf.

    Das ist absoluter Quatsch. Natürlich kann ein Setter Gültigkeitsprüfungen übernehmen. Nur weil er einfach nur Setter heisst, muss er ja nicht nur das tun. Da gibt es vielleicht Leute, die das so für sich festlegen, und dann als goldene Regel verkaufen wollen.


Anmelden zum Antworten