Stilistische Fragen zu Klassenaufbau und Namengebung

editOr

Mitglied
Hallo

Folgende Fragen sind zwar nicht weltbewegend, da sie mir aber immer wieder über den Weg laufen, dachte ich mir, ich frage mal hier nach:

1. Sollten alle Attribute einer Klasse schon bei der Erstellung bekannt sein?
Folgendes Beispiel: Angenommen wir haben eine Klasse Auto mit den Attributen: Farbe, Jahrgang, PS und Preis.

Ist es stilistisch unproblematisch einige Attribute vorerst ungesetzt zu lassen und erst später zu ergänzen. Häufig komme ich in Situationen in denen ich dem Konstruktor z.B. Farbe, Jahrgang und PS übergeben kann, der Preis aber noch nicht bekannt ist. Z.B. wird diese später und unter Berücksichtigung anderer Eigenschaften wie. z.B. Marktsituation bestimmt. Erst dann wird der Preis gesetzt.

Natürlich ist dies kein Problem, solange der vorerst noch leere Preis (oder Null) nicht verwendet wird, ich frage mich nur, ob solche Situationen prinzipiell vermieden werden sollen und wie man das macht. Eventuell ist es aber auch stilistisch kein Problem und der "Normalfall". Wäre froh um Klärung.

2. Die der Methodenpräfix "get" ist ja üblicherweise für Getter reserviert. Ich habe gelesen, dass Getter keine eigene Logik enthalten sollten. Häufig würde sich ein "get" Präfix als besten Bezeichner einer Methode eignen, ohne dass es ein Getter im eigentlichen Sinne ist.
Bsp. Ich rufe den Preis des Autos (gespeichert in €) als USD ab. Ich würde dann eine Methode "getPriceInUSD(exchangeRate)" o.Ä. schreiben. Wie ist hier die Konvention, sollte man "get" nur für Getter im engen Sinne verwenden und hier auf etwas wie "evalPriceInUSD(exchangeRate)" ersetzen?

Wie ihr sehr, Detailfragen, trotzdem interessant zu wissen, wie das die Profis so handhaben.

Danke und Gruss
editOr
 
Hiho grüß dich,

Zu 1. Es ist ganz normal, nicht alle Attribute einer Klasse sofort zu setzen. Das würde Setter überflüssig machen. Mach dir also keine Gedanken, das ist ok.

Zu 2. Die Meinungen gehen auseinander, was den Einsatz von Gettern und Setter angeht.
Schau dir dazu vielleicht mal den Begriff der Businessmethoden an.
Zu deinem Beispiel kann auch gesagt werden Auto.price() statt Auto.getPriceInUsd().

Wie gesagt Ansichtssache.

Grüße
 
Zu 1) Ein ausgezeichnetes Beispiel: Hier ist es ein Indiz, dass der Preis u.U. gar nicht in die Klasse "Auto" gehört. "Auto" sollte einen Gegenstand beschreiben, aber der Preis gehört an eine Ware. Was ist, wenn es unterschiedliche Preise für unterschiedliche Kunden gibt? Unterschiedliche Währungen? Gehört der Mehrwertsteuersatz an ein Auto? Aber dann doch auch an eine Flasche Tabascosauce - aber was hat die mit einem Auto gemeinsam? Alles Gründe, eine eigene Klasse "Ware" oder "Produkt" zu haben, die auf ein Auto mit seinen Eigenschaften verweist.

Es gibt gute Gründe, warum Objekte nicht vollständig initialisiert werden können, aber man sollte die Augen offen halten, ob sich dahinter nicht ein Vermischen verschiedener Konzepte verbirgt. Außerdem kann man oft eine Klasse unveränderlich (immutable) machen, wenn man alle Attribute "sofort" kennt - und das ist im Allgemeinen eine gute Sache, da niemand mit den Instanzen Dummheiten anstellen kann. Nicht umsonst sind viele Java-Standard-Klassen (wie String) unveränderlich, und da wo sie es nicht sind (etwa Date), treten regelmäßig Schwierigkeiten auf.
 
Hallo editOr,

1. Sollten alle Attribute einer Klasse schon bei der Erstellung bekannt sein?
Folgendes Beispiel: Angenommen wir haben eine Klasse Auto mit den Attributen: Farbe, Jahrgang, PS und Preis.

Ist es stilistisch unproblematisch einige Attribute vorerst ungesetzt zu lassen und erst später zu ergänzen. Häufig komme ich in Situationen in denen ich dem Konstruktor z.B. Farbe, Jahrgang und PS übergeben kann, der Preis aber noch nicht bekannt ist. Z.B. wird diese später und unter Berücksichtigung anderer Eigenschaften wie. z.B. Marktsituation bestimmt. Erst dann wird der Preis gesetzt.

Natürlich ist dies kein Problem, solange der vorerst noch leere Preis (oder Null) nicht verwendet wird, ich frage mich nur, ob solche Situationen prinzipiell vermieden werden sollen und wie man das macht. Eventuell ist es aber auch stilistisch kein Problem und der "Normalfall". Wäre froh um Klärung.

Ich bin der Meinung, dass man vermeiden sollte, dass Werte erst später gesetzt werden. Dein Beispiel kann man super bauen, ohne dass der Wert später gesetzt werden muss. Wenn du das Auto in einer Methode einmal mit und einmal ohne Preis brauchst dann würde ich 2 unterschiedliche Klassen machen.

Java:
class Auto{
   Color farbe;
   int jahrgang;
   int ps;
....
}

class Artikel{
    Auto auto;
    BigDecimal preis; 
}

void tuWasMitAuto(Auto auto){}
void hierWirdDerPreisGebraucht(Artikel artikel){}

Sobald du den Preis brauchst musst du explizit einen Artikel erzeugen, dieser muss immer einen Preis haben. So läufst du nicht Gefahr, dass auf den Preis zu früh zugegriffen wird.

2. Die der Methodenpräfix "get" ist ja üblicherweise für Getter reserviert. Ich habe gelesen, dass Getter keine eigene Logik enthalten sollten. Häufig würde sich ein "get" Präfix als besten Bezeichner einer Methode eignen, ohne dass es ein Getter im eigentlichen Sinne ist.
Bsp. Ich rufe den Preis des Autos (gespeichert in €) als USD ab. Ich würde dann eine Methode "getPriceInUSD(exchangeRate)" o.Ä. schreiben. Wie ist hier die Konvention, sollte man "get" nur für Getter im engen Sinne verwenden und hier auf etwas wie "evalPriceInUSD(exchangeRate)" ersetzen?

Getter dürfen durchaus Logik haben. Ich persönlich versuche durch den Präfix auszudrücken, ob eine Methode "teuer" ist.

get bedeutet dabei "Diese Methode macht nicht viel außer eine Referenz zurückzugeben".

calculate/compute verwende ich gerne um zu signalisieren "Hier wird etwas berechnet, rufe es nicht öfter als umbedingt nötig auf".

create bedeutet "Hier wird ein neues Objekt erzeugt, das kostet Speicher und Laufzeit".

Bei getPriceInUSD kann man sich streiten ob diese überhaupt in die Klasse Auto gehört. Ich würde eher dazu tendieren eine eigene Umrechungs-Klasse zu machen, da ein Auto nicht wissen muss wie man umrechnet. Vom Namen würde ich es eher compute nehmen. Ist aber ein reines Bauchgefühl.
 
Das hängt mal a) vom persönlichen Programierstil und b) von den verwendeten Frameworks ab. Viele Frameworks verlangen die Existenz eines Defaultkonstruktors, womit die Sache damit zum Teil schon entschieden ist.
 
Ich mag ja die g-/setter gar nicht!

Einerseits soll keine Logik eingebaut werden
aber andererseits: was für einen Sinn macht diese StandardKonstruktion?
Java:
privat int myValue;
public int getMyValue() {return myValue;}
public void setMyValue(int myValue) {this.myValue=myValue;}
Da greif ich doch lieber direkt auf das Attribut zu?

Ich weiß, das ich damit an den Grundfesten der Conventionen rüttle.
Mir ist schon klar, dass in manchen Fällen (wenn eben Logik eingebaut wird) ein Schutz der Variable sinnvoll ist.
Aber im StandardFall meistens eben nicht.
Und wenn die Klasse aus-konzipiert ist, dann weiß ich ja, ob der Schutz Logik enthalten muss.

(Ganz abgesehen vom Stack und Zeitverbrauch ...)
 
Um mal wieder mein Standard-BlaBlaBla loszuwerden:
JavaBeans sind keine "echten" Objekte, sondern nur "dumme" Datenstrukturen.
Die Regeln der OOP gelten eben höchstens bedingt für nicht-OOP Konzepte.
Nebenbei, JavaBeans sind auch keine POJOs 😉

Ansonsten:
"Wertobjekte" als Immutables sind eine sehr gute Sache, "Entitäten" dagegen können nicht immutable sein, aber dafür u.a. aus Wertobjekten zusammengesetzt sein.
Immutables kann man logischerweise nicht in einem ungültigen Zustand erzeugen.
 
Ich mag ja die g-/setter gar nicht!

Einerseits soll keine Logik eingebaut werden
aber andererseits: was für einen Sinn macht diese StandardKonstruktion?
Java:
privat int myValue;
public int getMyValue() {return myValue;}
public void setMyValue(int myValue) {this.myValue=myValue;}
Da greif ich doch lieber direkt auf das Attribut zu?

Ich weiß, das ich damit an den Grundfesten der Conventionen rüttle.
Mir ist schon klar, dass in manchen Fällen (wenn eben Logik eingebaut wird) ein Schutz der Variable sinnvoll ist.
Aber im StandardFall meistens eben nicht.
Und wenn die Klasse aus-konzipiert ist, dann weiß ich ja, ob der Schutz Logik enthalten muss.

(Ganz abgesehen vom Stack und Zeitverbrauch ...)

Conventions hin oder her, Setter und Getter haben ihren Sinn.
Und es kommt darauf an, von welchem Standardfall wir hier reden. Von einem kleinen Schulprojekt, um eine gute Note zu kriegen oder etwas Größerem wie etwa einer Library? Bei ersterem kannst man mann meinetwegen auf S/G verzichten. Aber ansonsten sind Setter IMO Pflicht!

1. können beim Eintritt in die Methode Ausnahmefälle behandelt werden (z.B.
Code:
ferrari.price = new BigDecimal("-3");
kann man bei
Code:
public BigDecimal price;
nicht verhindern und die Fehlersuche wird umso teurer!
2. Threads / Redundanz! Wenn du von überall in deinem Programm auf deine eine Variable zugreifst und später bemerkst, dass du gerne Threads parallel laufen lassen willst, musst du massenweise Code suchen und umschreiben. Bei einem Setter/Getter ist das eine Sache von... Sekunden.
3. Ein Außenstehender, der sich den Code ansieht, wird sich fragen, wann es "passend" ist, diesen Wert zu verändern / abzurufen etc. Ihm wird wahrscheinlich nicht klar, warum es keine Getter/Setter Methoden gibt und (falls ein wenig bewandert in Java) denken, dass es sich um eine "final" Variable handeln müsste oder was sonst nicht alles. Es stiftet einfach Verwirrung. Deshalb sind ja viele Conventions da.

Sicherlich gibt es noch mehr Gründe, die für S/G sprechen...

Zu deinem Beispiel: Bei
Java:
private int myValue;
public int getMyValue() {
    return myValue;
}
public void setMyValue(int myValue) {
    this.myValue=myValue;
}
ergibt es vielleicht noch nicht so viel Sinn, aber wenn es:
Java:
private int myValue;
private boolean changesPermitted;

public Boo(int value){
    myValue = encode(value);
    changesPermitted = true;
}

public int getMyValue() {
    return decode(myValue);
}
public void setMyValue(int value) {
    if(value < 0 || value > 99) throw new IllegalArgumentException("Value out of range! Must be between 0 and 99");
    if(!changesPermitted) throw new IllegalStateException("Changes during this state of the isntance's lifetime are not allowed! [more detailed message here]");
    this.myValue = encode(myValue);
}
so aussieht, dann sieht es ganz anders aus (fun fact)!
 
Zuletzt bearbeitet:
Ausserem erlauben getter/setter days hinzufügen von Aspekten (AOP).
Z.b. schnell mal einen loggingaspekt über die klasse legen um zu loggen wann die werte gelesen/geschrieben werden
 
Noch eine weitere Frage:
sollte man in der Klasse selbst getter und setter benutzen?
z.B.:
Java:
private int a;
private boolean b;

public Bsp(int a,boolean b){
     this.a = a;
     this.b = b;
}
//oder
public Bsp(int a,boolean b){
     setA(a);
     setB(b);
}
 
Ich benutze meistens die erste Variante.
Falls du allerdings im setter noch Logik drin hast die irgendwelche Prüfungen durchführt, dann würde ich die setter aufrufen. Allerdings solltest du darauf achten dass die Methoden die du im Konstruktor aufrufst final sind, sonst könntest du da unschöne nebeneffekte bekommen.
 
Bei Settern im Konstruktor verzettel ich mich immer, das würde ich wirklich nur in Ausnahmefällen verwenden. (Außer man macht es NUR so)
Ich hatte das mal und als ich dann nach der Deklaration einer Komponente suchte, war ich überzeugt, dass man Programm zaubern konnte. Nirgends zu finden, aber die Komponente tritt im App auf... Und nach 5-10 Minuten fiel mir auf: Setter!

Sehe auch abgesehen davon keinerlei besonderen Zweck darin!
 

Zurück
Oben