Getter und Setter
-
Exakt. Und erfahrungsgemäß braucht man in einem gutem Design nur selten überhaupt so etwas wie Getter oder Setter und praktisch nie beides, darum gern auch noch ein zweites Mal drüber nachdenken...das ist ähnlich wie mit Singleton: Nur weil irgendwie jeder es verwendet muss es noch lange nicht gut sein
-
Ein Grund der mir inner Schule eingefallen ist wäre der, dass man so besser Fehlerbehandlungen durchführen kann.
HRESULT SetBlub(BLUB* Blub); HRESULT GetBlub(BLUB* BlubOut) const;
Ohne Get und Set:
HRESULT Blub(BLUB* Blub); HRESULT Blub(BLUB* BlubOut) const;
Die zweite Variante wäre dann wohl so nicht wirklich möglich.
-
Wie gesagt, am Besten keines von beiden. Getter und Setter sind, vor allem bei zahlreichem Auftreten, ein Symptom von schlechtem Design (Kapselung geht flöten und so). Darum würd ich mir auch nicht den Kopf zusehr über Namensregeln dafür zerbrechen sondern lieber darüber wie ich richtig Designe...
-
Mit diesen "allgemeingültigen" Regeln richtet ihr aber denselben Schaden an, weil man dann auf einmal solche Gedanken hat wie zum Beispiel "Wie könnte ich das jetzt ohne machen, weil es ist schlecht, hat der eine gesagt".
Wenn ein Objekt einen öffentlichen Zustand hat und es Nebenwirkungen hat (beispielsweise auf interne Zustände), dann benutzt man halt Getter und Setter, egal, ob dort nun das Wort Set oder Get auftaucht oder nicht. Es ist nichtmal gesagt, dass der Wert den man da übergibt oder bekommt eine 1:1-Entsprechung zu einem privaten Datenmember hat oder nicht, für mich bleibt's nen Getter oder Setter, in der Tat könnte ich mir eine Schnittstelle zu etwas mit einem Zustand gar nicht ohne vorstellen.
-
Decimad schrieb:
Wenn ein Objekt einen öffentlichen Zustand hat und es Nebenwirkungen hat [...]
Wenn du mich fragst hast du dann ein Designproblem. Ich gehe zumindest eben mal davon aus dass wir hier von einer Abstraktionsebene reden auf der man nach den Prinzipien der OOP arbeitet (also eher weiter oben). Getter und Setter machen auf anderen Ebenen (weiter unten) vereinzelt evtl. durchaus Sinn, z.B. die .size() Methode bei den Standardcontainern. Aber spätestens wenn Getter und Setter für den gleichen Zustand die Bühne betreten würde ich auch dort zumindest zweimal Nachdenken was ich da eigentlich grad tu.
Auch hab ich nicht gesagt dass es eine allgemeingültige Regel ist dass jeder Getter/Setter immer sofort schlecht ist. Ich hab gesagt
dot schrieb:
Getter und Setter sind, vor allem bei zahlreichem Auftreten, ein Symptom von schlechtem Design
Man beachte den Begriff Symptom. Was ich also gesagt habe ist dass, wenn man in seinem Code Getter und Setter sieht, dies ein Anzeichen dafür ist dass man sehr wahrscheinlich ein schlechtes Design verfolgt und daher vielleicht etwas dagegen unternehmen sollte. Nirgendwo hab ich gesagt dass Getter und Setter explizit und immer bedeuten dass das Design schlecht ist. Aber, vor allem bei gehäuftem Auftreten, kann man sicherlich sagen dass die Wahrscheinlichkeit dass es sich um schlechtes Design handelt steigt...
-
@dot! Ja ja, braucht man nicht.
Wir hatten damals hier schon im Forum jemanden, der das behauptet hat. Und als er doch bitte an produktiven Beispielen eine Lösung für "ohne getter und setter" zeigen sollte, mußte er feststellen, das es völliger Humbug ist.
Zeig uns doch mal bitte, wie man Objekte ohne Getter/Setter behandeln kann. Wie kann ich denn bitte z.B. ein geometrisches Objekt z.B. verschieben?
Sag mir bitte nicht, das die Methode "move(int, int)" heißen soll. So doof sind wir nicht.
-
Artchi schrieb:
Zeig uns doch mal bitte, wie man Objekte ohne Getter/Setter behandeln kann. Wie kann ich denn bitte z.B. ein geometrisches Objekt z.B. verschieben?
Sag mir bitte nicht, das die Methode "move(int, int)" heißen soll. So doof sind wir nicht.
Versteh mich nicht falsch. Ich sage nicht dass Getter und Setter immer schlecht sind. Doch nur weil du ein Beispiel angeben kannst bei dem Getter und Setter sinnvoll sind bedeutet das noch lange nicht dass das immer so ist, genausowenig wie Getter und Setter immer bedeuten dass etwas falsch ist...
Ich denke wir sind uns einige dass solche Getter und Setter wie im Anfangspost gezeigt praktisch nie eine Gute Idee sind. Kombiniert mit der Tatsache dass er offenbar auf der Suche nach einer Namenskonvention für derartige Konstrukte ist führt mich das zu der Annahme dass der OP wohl vorhat den großteil seiner Klassen nach einer derartigen Philosophie zu designen. Ich denke du wirst mir zustimmen das dies ganz sicherlich nicht zu einem gutem Design führen wird...
-
Es gibt natürlich auch Klassen, die Membervariablen haben, die sich indirekt ändern. Aber letztendlich werden diese indirekten Änderungen durch Getter/Setter anderer Membervariablen ausgelöst. Mir fallen ganz wenige Beispiele ein, wo es keine Parameterübergabe gibt. Banales Bsp.:
class A { int i; public: void up() { i++; } };
Aber letztendlich ist die Mehrheit der Objekte so, das man Parameter an Memberfunktionen übergibt, die eine direkte oder indirekte Auswirkung auf Memebervariablen haben. Und das sind dann Setter, da ich durch einen Parameter direkt oder indirekt das Objekt ändere. Ob ich das set, move, <<, open u.ä. nenne, ist reine Vereinfachung für den User der Schnittstelle/Klasse. Was auch gut ist! Am Ende passiert aber überall das gleiche.
Das was ich aber immer sage: erstelle Memberfunktionen nach Bedarf! Aber wenn eine Klasse alles voller Getter/Setter hat, gehe ich nicht mit dem Vorurteil ran, das sie schlecht ist, da vielleicht einfach diese Getter/Setter nötig waren? Es bleibt einem natürlich gestattet, es nachträglich zu prüfen. Aber bitte erst prüfen und dann ein Urteil bilden.
-
dot schrieb:
Ein Design das auf Gettern und Settern basiert schaut einfach komplett anders aus als eines das ohne auskommt...
Das Problem ist, dass es lange nicht immer ein Design gibt, das ohne auskommt, und trotzdem besser als mit ist. Was man aber sicher vermeiden sollte, ist eine apriori 1:1 Abbildung von Datenmembern zu Set/Get-Funktionen.
Shade Of Mine hat das einmal gut auf den Punkt gebracht (auch wenn
goto
vielleicht nicht das beste Beispiel ist):Shade Of Mine schrieb:
Das Problem mit getter/setter ist wie mit goto. Zuviel verwenden ist furchtbar aber gleich garnicht verwenden ist mindestens genauso furchtbar.
Getter/Setter werden liebend gerne für alle internen Variablen genommen aber nur ein Bruchteil des internen States hat nach aussen zu gelangen (aber dieser Bruchteil muss nach aussen).
-
Natürlich haben Objekte einen internen Zustand, das genau ist doch der Sinn von Objekten.
Artchi schrieb:
Aber letztendlich ist die Mehrheit der Objekte so, das man Parameter an Memberfunktionen übergibt, die eine direkte oder indirekte Auswirkung auf Memebervariablen haben. Und das sind dann Setter, da ich durch einen Parameter direkt oder indirekt das Objekt ändere.
Ist push_back deiner Meinung nach ein Setter für die Größe eines std::vector?
Artchi schrieb:
Aber wenn eine Klasse alles voller Getter/Setter hat, gehe ich nicht mit dem Vorurteil ran, das sie schlecht ist, da vielleicht einfach diese Getter/Setter nötig waren?
Wenn eine Klasse alles voller Getter und Setter hat könnte sie wohl meistens auch gleich besser Teil der Implementierung von einem anderen Objekt oder ein einfaches struct sein. Das fällt imo dann aber schon unter die Ebene auf der man sich normalerweise mit OOP bewegt...
Nexus schrieb:
Was man aber sicher vermeiden sollte, ist eine apriori 1:1 Abbildung von Datenmembern zu Set/Get-Funktionen.
Und genau um die gings mir in meinen Aussagen vorrangig...und ich hab wie gesagt auch nirgendwo behauptet dass man im Allgemeinen niemals Getter/Setter verwenden darf...
-
dot schrieb:
Ist push_back deiner Meinung nach ein Setter für die Größe eines std::vector?
Ja, weil ich durch den push_back-Parameter, das übergebene Objekt in den Container setze (direkte Änderung). Die Size ist eine indirekte Änderung.
dot schrieb:
Wenn eine Klasse alles voller Getter und Setter hat könnte sie wohl meistens auch gleich besser Teil der Implementierung von einem anderen Objekt oder ein einfaches struct sein. Das fällt imo dann aber schon unter die Ebene auf der man sich normalerweise mit OOP bewegt...
Wetten nicht?
Beispiel gefällig?
struct Point { int x, y; };
Wo ist hier der Nachteil? Wenn mir später im Projektverlauf einfällt, das ich eigentlich negative Werte vermeiden will, und bestimmte maximale Werte definieren will, habe ich im Projektverlauf ein Problem.
Ich kann die Wert-Abfragen natürlich in den Klassen machen, die Point verwenden. Aber das könnte schon viele werden. Ich könnte aber einfach Point-Members kapseln. Aber dann muß der Client-Code geändert werden.
Also mache ich gleich von Anfang an ein Klasse:
class Point { int _x, _y; public: void x(int a) { _x = a; } void y(int a) { _y = a; } };
Im Projekt kann ich später, wenn mir was einfällt, eine Änderung vornehmen, ohne den Client-Code ändern zu müssen.
class Point { int _x, _y; public: void x(int a) { if(a >=0 && a < 100) _x = a; } void y(int a) { if(a >=0 && a < 100) _y = a; } };
Aber Kapselung ist ein alter Hut. Da erzähle ich sicherlich nichts neues?
Und in C++ kostet diese Kapselung nichts, wenn es non-virtual ist.
Klar, es gibt die Möglichkeit, das andere Objekte die Abfrage machen:
class Sprite { Point coord; // Struct! public: void position(Point &p) // auch ein Setter { if( p.x >= 0 && p.x < 100 && p.y >= 0 && p.y < 100 ) coord = p; } void move(int x, int y) // indirekter Setter { coord.x += x; coord.y += y; } };
Aber du weißt halt auch nicht, wo du vielleicht überall die Abfragen rein machen mußt.
Aber selbst das Sprite::position(Point) ist ein Setter! Es setzt den coord-Wert.
Wir kommen immer wieder dahin zurück, das wir Getter/Setter benötigen.
-
Artchi schrieb:
dot schrieb:
Ist push_back deiner Meinung nach ein Setter für die Größe eines std::vector?
Ja, weil ich durch den push_back-Parameter, das übergebene Objekt in den Container setze (direkte Änderung). Die Size ist eine indirekte Änderung.
Wenn du getter/setter so definierst, kann man selbstverständlich nichts ohne sie programmieren.
Versteht man darunter aber eher so was
Wikipedia schrieb:
Eine Zugriffsfunktion, auch Zugriffsmethode oder Akzessor, ist in der objektorientierten Programmierung eine spezielle Methode, die eine einzelne Eigenschaft eines Objekts abfragt oder ändert.
würde ich durchaus dazu raten, sie sparsam einzusetzen.
-
Artchi schrieb:
Ja, weil ich durch den push_back-Parameter, das übergebene Objekt in den Container setze (direkte Änderung). Die Size ist eine indirekte Änderung.
Dieser Argumentation nach müsste aber alles ein Setter oder Getter sein. Denn alles liefert Informationen über den Status oder hat Seiteneffekte. Und damit ist es auch nicht mehr schwierig zu begründen, dass man Setter und Getter immer braucht, sobald man Funktionalität benötigt.
Artchi schrieb:
Beispiel gefällig?
Manchmal sind aber rohe ungekapselte Strukturen besser (weil einfacher und weil keine relevanten Nachteile). Gerade Vektorklassen halte ich für so ein Beispiel.
-
dot schrieb:
[...]
Ist push_back deiner Meinung nach ein Setter für die Größe eines std::vector?[...]Wenn die Standardbibliothek als Beispiel gegen Setter/Getter herhalten soll, dann geht das ziemlich nach hinten los. Neben den indirekten Settern wie push_back gibt es auch jede Menge direkte Setter und passende Getter dazu.
Beimstd::vector
z.B.:
`reserve() <-> capacity()resize() <-> size()
operator[] <-> operator[]
at() <-> at()
push_back() <-> back()
` etc...
-
Nexus schrieb:
Manchmal sind aber rohe ungekapselte Strukturen besser (weil einfacher und weil keine relevanten Nachteile). Gerade Vektorklassen halte ich für so ein Beispiel.
Ja, Containerklassen sind schon eine Sonderstellung. Was wieder belegt, das man nicht generell sagen kann "Wenn ich DAS sehe, ist das schlecht!".
Das mit den "offenen" Containerklassen steht übrigens auch (wenn ich mich nicht komplett täusche) auch im Scott Meyers drin. Weil man die Objekte nun mal wieder als Reference zurück geben muß. Aber z.B. die Size von einem Vector kann ich nicht direkt ändern.
-
Nexus schrieb:
Artchi schrieb:
Beispiel gefällig?
Manchmal sind aber rohe ungekapselte Strukturen besser (weil einfacher und weil keine relevanten Nachteile). Gerade Vektorklassen halte ich für so ein Beispiel.
Insbesondere POD-Strukturen sind mir in manchen Fällen lieber als eine Klasse, die die gleiche Funktionalität erfüllt. Der Grund ist vermutlich offensichtlich
-
Tachyon schrieb:
dot schrieb:
[...]
Ist push_back deiner Meinung nach ein Setter für die Größe eines std::vector?[...]Wenn die Standardbibliothek als Beispiel gegen Setter/Getter herhalten soll, dann geht das ziemlich nach hinten los. Neben den indirekten Settern wie push_back gibt es auch jede Menge direkte Setter und passende Getter dazu.
Beimstd::vector
z.B.:
`reserve() <-> capacity()resize() <-> size()
operator[] <-> operator[]
at() <-> at()
push_back() <-> back()
` etc...
Alle von dir genannten Funktionen (außer evtl. size und capacity) machen sehr viel mehr als einfach nur Getter/Setter für Interna des vectors zu sein.
-
SeppJ schrieb:
Alle von dir genannten Funktionen machen sehr viel mehr als einfach nur Getter/Setter für Interna der Klasse zu sein.
Der Sinn von Gettern ist doch nicht die bloße Rückgabe eines Wertes, sondern gerade dass man das Abfragen/Setzen eines Wertes mit einer Aktion oder Konvertierung verbinden kann und das diese Dinge vor dem Client verborgen bleiben.
-
SeppJ schrieb:
Alle von dir genannten Funktionen (außer evtl. size und capacity) machen sehr viel mehr als einfach nur Getter/Setter für Interna des vectors zu sein.
Und woher weißt du das? Doch nur weil du dich mit der Implementierung beschäftigt hast. Aber das ist bei Kapselung für Client-Code nicht nötig. Ich muß nur wissen, was gemacht wird, und nicht wie. Deshalb sind Funktionsnamen nur Schall und Rauch. Deshalb ist am Ende alles Setter/Getter.
Auch die Size könnte nicht einfach nur ausreturn m_size;
bestehen. Sie könnte auch stattdessen einfach alle Elemente on-the-fly durch zählen. Wäre zwar unperformant, aber ist es dann kein Getter mehr?
-
Tachyon schrieb:
SeppJ schrieb:
Alle von dir genannten Funktionen machen sehr viel mehr als einfach nur Getter/Setter für Interna der Klasse zu sein.
Der Sinn von Gettern ist doch nicht die bloße Rückgabe eines Wertes, sondern gerade dass man das Abfragen/Setzen eines Wertes mit einer Aktion oder Konvertierung verbinden kann und das diese Dinge vor dem Client verborgen bleiben.
Die hier diskutierten Getter und Setter aber gerade nicht! Das ist doch gerade das was dot aussagen will. Getter und Setter die einfach nur private member quasi-public machen sind oft unnötig und oft Zeichen schlechten Designs. Memberfunktionen die tatsächlich Arbeit verrichten und dabei private Member ändern (eventuell auch nur einen) sind hingegen Musterbeispiele für objektorientiertes Design.