Getter und Setter
-
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.
-
Eben, Getter und Setter sind eben alles was einen abstrakten Zustand eines Objektes erfragt oder verändert (am besten in einem für die Anwender zweckdienlichen weg). Ein Objekt ohne Zustand wäre irgendwie sinnlos.
Es sollte eher ein spezieller Name für Methoden erfunden werden, die wirklich nur einen Datenmember verändern und auslesen und somit die Interna nach außen tragen. Dazu könnte man dann nämlich sagen, was hier vorher so allgemeingültig falsch behauptet wurde.
-
SeppJ schrieb:
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.
Wobei man hier anmerken muss, dass es sich auch so ergeben kann, dass dem so ist. Es ist kein Zeichen von schlechtem Design, nur weil die Getter und Setter trivial sind! Wenn ein gewisser Zustand nun eben sehr einfach in die übrige Modellierung des Objekts hineinpasst, warum sollte man sich da Sorgen machen und sich nicht einfach nur freuen?
-
Artchi schrieb:
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.
Nein, habe ich nicht. Aber wie soll push_back ein setter sein? Da muss kopiert werden, Zeiger gesetzt werden, eventuelle Zähler erhöht werden, wenn's schlimm kommt sogar neu allokiert und alles kopiert werden. Das steht alles in den Anforderungen an vector. Es ist unmöglich das durch Änderung einer Variablen zu erreichen.
Ebenso reserve und resize.
operator[] muss mindestens ein bisschen Pointerarithmetik machen. at() zudem vergleiche durchführen.
capacity und size könnten Getter sein, weil die Anforderung an diese ist, dass sie einfach einen Wert zurückgeben. Da kann man nicht wissen, ob dieser Wert nicht als privater Member gespeichert wird (da weiß ich aus der Implementierung, dass dies oft nicht so ist, aber das ist egal da ich ja nicht bestreite, dass die beiden keine getter wären).
Aber gerade push_back, reserve und resize sind Gegenbeipiele zu Settern.
-
Decimad schrieb:
Eben, Getter und Setter sind eben alles was einen abstrakten Zustand eines Objektes erfragt oder verändert (am besten in einem für die Anwender zweckdienlichen weg). Ein Objekt ohne Zustand wäre irgendwie sinnlos.
Es sollte eher ein spezieller Name für Methoden erfunden werden, die wirklich nur einen Datenmember verändern und auslesen und somit die Interna nach außen tragen. Dazu könnte man dann nämlich sagen, was hier vorher so allgemeingültig falsch behauptet wurde.Ja, ich glaube, dass dies das Problem in diesem Thread ist. Verwirrung durch Mängel in der menschlichen Sprache.
-
SeppJ,
was ergibt es für einen Sinn solch eine Unterscheidung zu treffen?
Sagen wir mal, wir haben eine Implementierung I von Klasse X und eine Implementierung J von Klasse X. Beide Implementierungen könnten die Daten komplett anders vorhalten. Während Methode Y bei Implementierung I dann ein Getter wäre, wäre sie in Implementierung J kein Getter mehr. Das würde bedeuten, dass es Implementierungsabhängig ist, ob Methode Y von Klasse X ein Getter ist oder nicht.
-
SeppJ schrieb:
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.
Grundsätzlich führt man Getter/Setter doch gerade deshalb ein, weil man die Freiheit haben will, später irgendwelche Implementierungsdetails zu ändern, ohne das der Clientcode angepasst werden muss. Das es schlecht ist, prinzipiell jeden privaten Member über Setter/Getter public zu machen, ist wohl klar. Dann kann man sich auch gleich das private sparen.
Mir erschließt sich jedoch nicht, wie Du aus der Frage des TO schließen willst, dass es um das prinzipielle publikmachen von Klasseninterna über Setter/Getter geht. Ich lese daraus eine Frage nach der Namenskonvention, und nicht mehr und nicht weniger.SeppJ schrieb:
Aber gerade push_back, reserve und resize sind Gegenbeipiele zu Settern.
Wieso insbesondere
reserve
undresize
Gegenbeispiele für Setter sind, erschließt sich mir auch nicht. Wenn Du Setter als bloßes Mittel zum setzen einer Variable auf einen Wert siehst, dann hast Du den Grund dafür irgendwie nicht verstanden. Aus Client-Sicht tue ich nämlich genau das: Ich setze etwas auf einen gewünschten Wert.
Es gibt da so ein tollen Beispiel für eine Klasse für komplexe Zahlen. Dort wird die Implementierung von Polarkoordinaten auf kartesische Koordinaten umgestellt, weil dem Autor aufgefallen ist, dass dies in seinem Kontext effizienter ist. Das Interface ändert sich dabei nicht. Auch nicht die Tatsache, dass esreal
undimag
sowie für Betrag und Winkel Setter und Getter gibt.
-
So, und ich denke, worauf ihr abzielt ist eine ganz andere Aussage. Ein öffentlicher Getter/Setter gibt dem Benuter eine Garantie, dass ein solcher Zustand in irgendeiner Form von Objekten dieser Klasse verwaltet wird. Wenn man also unnötige Getter/Setter anbietet, dann muss man umso mehr Arbeit reinstecken, wenn man die Implementierung später ändert, um die Garantien, die die Schnittstelle gibt weiterhin zu erfüllen.
-
Decimad schrieb:
So, und ich denke, worauf ihr abzielt ist eine ganz andere Aussage. Ein öffentlicher Getter/Setter gibt dem Benuter eine Garantie, dass ein solcher Zustand in irgendeiner Form von Objekten dieser Klasse verwaltet wird. Wenn man also unnötige Getter/Setter anbietet, dann muss man umso mehr Arbeit reinstecken, wenn man die Implementierung später ändert, um die Garantien, die die Schnittstelle gibt weiterhin zu erfüllen.
Versteh ich nicht. Wenn auf die Attribute zugegriffen werden muss, hat man den Aufwand doch so oder so und wenn man mit anderen Methoden irgendwas erreichen kann, wird man die doch (vermutlich) sowieso verwenden.
Ich würde mich jetzt aber auch nicht so auf die Begriffe Getter/Setter einschießen. Entspricht das irgend einem irgendwo definierten Terminus? Würde ich jetzt inuitiv verneinen.