Werte setzen: Konstruktor vs. Setter-Methode

Hallo Forum,

hier eine kurze Verständnisfrage: Ich habe zahlreiche Klassen erstellt, bei denen Objekteigenschaften bei der Instanzierung im Konstruktor festgelegt werden und die zusätzlich Setter-Methoden enthalten, um die Eigenschaften nachträglich zu verändern. Nachdem ich nun angefangen habe, benutzerdefinierte Exceptions zu verwenden, stelle ich fest, dass ich doppelte Arbeit habe, weil ich in den Konstruktoren und in den Setter-Methoden Exceptions werfen müsste. Daher meine Überlegung: Wäre es aus Übersichtsgründen sinnvoll, die Objekteigenschaften hier nur über die Setter-Methoden festzulegen. Dann hätte ich zwar bei der Objekterzeugung einie Zeilen mehr infolge des Aufrufs der Setter-Methoden, aber der Code würde dadurch auch lesbarer und die Wahrscheinlichkeit einer unvollständigen Fehlerbehandlung geringer. Oder hätte es abgesehen von der einzeiligen Objekterstellung weitere Vorteile, dem Konstruktor die entsprechenden Parameter gleich zu übergeben? Was meint Ihr?

Wenn du einen Setter hast, dann benutze diesen…auch im Konstruktor.

Auf diese Idee bin ich gar nicht gekommen, wie peinlich!
Danke vielmals! :slight_smile:

Aber nebenbei auch immer überlegen, ob die setter wirklich gebraucht werden. Wenn alles schon im Konstuktor übergeben werden kann (und die Instanzvariablen dann final sein können) hat das auch Vorteile: Der Zustandsraum wird dramatisch verkleinert, d.h. man weiß, dass ein Objekt nicht in einem “ungültigen Zustand” sein kann, weil ein setter falsch (oder noch gar nicht) aufgerufen wurde. Wenn es um viele Eigenschaften geht, kann man auch ein Builder-Pattern in betracht ziehen.

Ich kann es jetzt nicht wörtlich zitieren, aber in “Growing Object-Oriented Software, Guided by Tests” verfolgen die Autoren die Strategie, sowohl Getter als auch Setter so lange wie möglich zu vermeiden.
Für mich klingt das auch einleuchtend, denn der Zustand eines Objektes wird dann
[ol]
[li]nicht veröffentlicht
[/li][li]nur durch Methoden verändert, die sprechend bezeichnet wurden
[/li][/ol]

[QUOTE=cmrudolph]Ich kann es jetzt nicht wörtlich zitieren, aber in “Growing Object-Oriented Software, Guided by Tests” verfolgen die Autoren die Strategie, sowohl Getter als auch Setter so lange wie möglich zu vermeiden.
Für mich klingt das auch einleuchtend, denn der Zustand eines Objektes wird dann
[ol]
[li]nicht veröffentlicht[/li][li]nur durch Methoden verändert, die sprechend bezeichnet wurden[/li][/ol][/QUOTE]
generell - und das leuchtet den meisten ein - sollte man keine Code haben, den man nicht braucht. Ergo Code erst erstellen, wenn jemand einen dazu zwingt. Das schliesst getter und setters ein.
Im Grunde gilt folgende simple Regel:

Alles (Klassen, Methoden, Variablen) sind private final solange bis man dazu genoetigt wird dies zu aendern

Solange es dafür noch keine Mechanismen gibt: Ein „+1“ für obigen post :smiley: Dass manche der Versuchung erliegen zu scheinen, dieses schreeecklich praktische Eclipse-Funktion „Generate Getter and Setters“ pauschal und unreflektiert bei jeder Klasse anzuschmeißen ist irgendwie beunruhigend…

Ich war bisher auch immer ein Fan davon, möglichst alle Member “privat final” zu haben. Das änderte sich aber schlagartig, als ich mich Bildern und verschiedenen Formaten beschäftigt habe und bei diesen dann Tilesets und Filter ala Java2D implementieren wollte.

  1. Normalerweise haben Bilder (und zwar alle) eine feste Höhe, eine feste Breite und ein festes Pixelformat, kurzum, alles eigentlich Kandidaten für “private final”.
  2. Wenn das nun alles “private final” wär, könnten sich gescalete oder Teilbilder weder Daten, Pixelformate, Höhen und Breiten teilen, “private final” wär also eine äusserst schlechte Idee.
  3. Da nun aber nicht unbedingt alles von aussen verändert werden soll (bei Bildern wären es z.B. nur einzelne Pixelfarben), kann man sich immer noch überlegen, wöfür man Getter und/oder Setter definiert. Bei Bildern wäre es z.B. eine schlechte Idee, mal eben das Pixelformat zu ändern, weil daduch bei gleichbleibender Grösse unter Umständen der Datenpuffer nicht mehr passt (z.B. von RGB zu RGBA also 24/32 Pixelbits).

Fazit: Es kommt immer auf die speziellen Anforderungen eines Typs an. Deswegen definiere ich überwiegend alles zunächst alle Member als “public/protected final” oder “private” und erstelle für die privaten dann public Getter und evtl. auch gleich protected Setter, wobei letztere dann in Klassenerweiterungen durchaus auch “public” gemacht werden können. Diese unheimlich praktische Eclipse-Funktion benutze ich lediglich via Codevervollständigung, wo sie entsprechende Vorschläge macht, wenn man an den richtigen Stellen ein “s” oder ein “g” tippt. Natürlich: sobald ich für einen Member einen Setter implementiere, wird der restliche Code auch dahingehend refactored.

Ich muss sagen ich versteh dein Bild bsp nicht so ganz hier, es ist ein beliebiger use case fuer irgendwas… aber egal. Das Wort „zunaechst“ in dem Beitrag find ich irritierend. Das klingt nach „ich mach eine Klasse und dann sofort alles erstmal so“, ohne einen konkreten Verwender der einem das aufzwingt.

Nochmals: Eine Klasse sollte aus einem einfachen Grund geschrieben werden → Jemand braucht sie. Dieser Jemand gibt einem vor, welche Funktionalitaet gebraucht wird und in welcher Sichtbarkeit. Mehr sollte man nicht machen. Braucht die Klasse weitere Funktionalitaet intern, so ist das alles private final zu machen. Wenn man dann irgendwie auf einen ungluecklichen Compiler stoesst, loest man das.

Es ist immer einfacher Sichtbarkeiten/finals zu pushen anstatt sie spaeter einzufuehren/zu verringern. Also hier ein Aufruf zur Faulheit: nicht mehr machen als man muss, nicht weiterdenken als man gerade soll (polemisch!) → seid faul, programmieren kann somit so einfach sein

[QUOTE=bygones]Ich muss sagen ich versteh dein Bild bsp nicht so ganz hier, es ist ein beliebiger use case fuer irgendwas… aber egal. Das Wort “zunaechst” in dem Beitrag find ich irritierend. Das klingt nach “ich mach eine Klasse und dann sofort alles erstmal so”, ohne einen konkreten Verwender der einem das aufzwingt.[/QUOTE]Dann für dich mal ein wenig konkreter:
Jemand benötigt eine Bilder-Klasse. Ein Bild hat mindestens einen Pixelbuffer, ein Pixelmodell, eine Breite und eine Höhe. Das kann man im Prinzip alles final machen, wäre aber von vorne herein eine ganz schlechte Idee, wenn man es irgendwann um Image-Tiling, Scaling und anderer Filter erweitern soll/will. Selbst wenn ein Verwender die Internals alle so haben will, würde ich ihn dahingehend zumindest schon mal beraten und wenn er dagegen Resistent ist, darf er sich auch gerne einen anderen Programmierer suchen, der nachträglich jene “Freiheiten” einpflegt, die zu einer gewünschten Erweiterung notwendig wären. Soviel Unterschied ist ja zwischen “private” und “private final” gar nicht.

siehe Signature: You should never assume… man sollte nie programmieren weil man der Meinung ist man wisse wie man in Zukunft eine Klasse gestalten wird (aber das ist eine andere Diskussion !)

Was ist so schlimm daran alles zb als private final zu machen und dann wenn du Erweitern musst, zb final wieder zu loeschen ? wie schon gesagt, ein final kann immer geloescht werden, nicht immer hinzugefuegt werden.

Das was ich hier propagiere widerspricht ueberhaupt nicht deinem Use Case hier. Ich sag ja: Wenn ein konkreter Bedarf besteht, dann sollen private final etc ueberdacht/geandert werden, aber nicht vorher. Wenn es keiner braucht, macht man keine setter/getter, man macht keinen unnoetigen code etc, und so eben auch macht man alles private final.

[QUOTE=bygones]Was ist so schlimm daran alles zb als private final zu machen und dann wenn du Erweitern musst, zb final wieder zu loeschen ? wie schon gesagt, ein final kann immer geloescht werden, nicht immer hinzugefuegt werden.[/QUOTE]Das Ding ist, dass ich mir um diese “final” gar keinen Kopf mehr machen muss. Ein Use-Case, wann man unbedingt “private final” braucht, eschliesst sich mir nicht, deswegen lasse ich es in der Regel halt gleich weg.

Das Entscheidende ist, dass du damit eine Einschränkung wegläßt. Damit entsgeht ein Freiheitsgrad (nämlich die Variable zu ändern). Und DIESEN kann man im Nachhinein dann nicht mehr wegnehmen, weil jemand diesen Freiheitsgrad nutzen wird. Ich denke auch, dass man so konservativ wie möglich sein sollte - also alles so private und final wie möglich machen.

Die Grundsatzfrage ist doch: unveränderliche Klasse oder nicht? Ein einziger Setter, und schon ist die Katze aus dem Sack. Es gibt natürlich valide Gründe für veränderliche Klassen (hauptsächlich Performance), aber wenn die nicht vorliegen, sollte man sich sehr genau überlegen, ob es die vermeintliche Bequemlichkeit wert ist, Unveränderlichkeit aufzugeben. Was oft übersehen wird ist, dass sich unveränderliche Objekte Datenstrukturen teilen können, so dass sich der Kopieraufwand als viel kleiner erweist als gedacht.

Wie Landei schon sagte, ist das bei Immutables der Fall.
Da will man dann immer private final, ist ja kein Problem, Mutatoren veraendern das Objekt nicht wirklich, sondern erzeugen ein neues.
Funzt ja prima bei String etc. pp.

Bin auch seit dem ich mit Scala angefangen habe dazu übergegangen so viel wie möglich in meinem Programm als final zu deklarieren, (auch Klassen aber dass ist ein anderes Thema). In meinem aktuellen Project muss ich relativ viel mit Threads machen und durch die immutable Datenobjecte die ich hier habe muss ich mir meistens keine Gedanken bzgl. race conditions machen. Ist auch ein Vorteil wenn man Immutables hat.

getter und setter füheren oft zu feature envy, also dass etwas außerhalb der Klasse gemacht wird, was sie eigenlich selbst machen sollte.

bye
TT

[QUOTE=Marco13]Das Entscheidende ist, dass du damit eine Einschränkung wegläßt. Damit entsgeht ein Freiheitsgrad (nämlich die Variable zu ändern). Und DIESEN kann man im Nachhinein dann nicht mehr wegnehmen, weil jemand diesen Freiheitsgrad nutzen wird. Ich denke auch, dass man so konservativ wie möglich sein sollte - also alles so private und final wie möglich machen.[/QUOTE]+ @all:
Das Entscheidende ist doch, dass ein einzelnes “private” in der Klasse so lange “final” ist, bis es von der Klasse selbst oder per Reflection bzw JNI verändert wird. Auf die letzteren beiden nehme ich aber keine Rücksicht. Das bedeutet aber nicht, dass ich niemals “private final” verwende jedoch wird das immer seltener.

Per Reflection ist eine Sache, per JNI oder sonstige native Hooks ließe sich die Variable aber eh wieder manipulieren, wie man lustig ist. Da hätte final also ohnehin keine Wirkung, weshalb man da eh keine Rücksicht drauf nehmen kann :wink:

Was ich bei final schade finde: Wenn ich länger Dinge, die ich im Konstruktor initialisieren möchte, in Methoden auslagere, darf ich die dort initialisierten Dinge nicht mehr final machen.