OOP Fragen zu Programmierstil

Aphotic

Neues Mitglied
Hallo Java-Forum! Ist mein erster Post hier. 🙂

Ich muss ein Programm für die Uni schreiben, was auch funktioniert, nur habe ich noch ein paar Fragen zu OOP / Programmierstil (wird auch bewertet, außerdem interessiert es mich sowieso):

1.
Angenommen, ich habe z.B. eine LinkedList allThings mit Dingen, welche durch eine ID eindeutig identifizierbar sind und will eine einfache Funktion getThing(int id) schreiben, die das Ding mit diese ID aus der Liste heraussucht und returnt (wenn nicht vorhanden, return null). Dann geht an sich ja einfach mit einem break oder zwei returns:
Java:
public Thing getThing(int id) {
	Thing returnedThing = null;
	for (Thing thing : allThings) {
		if (thing.getID() == (id)) {
			returnedThing = thing;
			break;
		}
	}
	return returnedThing;
}
bzw.
Java:
public Thing getThing(int id) {
	for (Thing thing : allThings) {
		if (thing.getID() == (id)) {
			return thing;
		}
	}
	return null;
}
Nur heißt es ja, dass breaks und zusätzliche returns verpöhnt sind. Ist das in einem solchen Fall OK, oder wie soll ich das am besten umgehen? Oder gleich HashMaps benutzen? Wenn ich mit dem ListIterator durch ne while-Schleife iteriere müsste ich ja in der Abbruchbedingung prüfen, ob die Liste noch ein Element hat und ob das Element das gesuchte ist und müsste nach der Schleife nochmal überprüfen, ob sie das Element gefunden und abgebrochen oder nur alle durchlaufen und abgebrochen hat, was ja umständlich wäre...

2.
Soll man, wenn man innerhalb einer Klasse auf deren Attribute / Methoden zugreift, immer "this" davorschreiben (Lesbarkeit), oder nur, wenn es nötig ist?

3.
Angenommen, ich will ein neues Ding erzeugen und zur Liste hinzufügen, aber mit der Einschränkung, dass die ID eines Dings nur genau 8-stellig sein darf:
Java:
public void addThing(int id) {
	allThings.add(new Thing(id));
}
Außerdem hat mein Programm eine Shell, die z.b. den Befehl "addthing x" kennt (x ist die ID). In der Shell wird überprüft, ob x überhaupt ein Integer ist usw.. Ist es in Ordnung, wenn ich in der auch Shell üperprüfe, ob diese ID 8-stellig ist, dann könnte ja durch die public-Methode addThing(int id) theoretisch auch Dinge mit nicht 8-stelligen IDs anlegen. Was aber dadurch verhindert wird, dass man nur über die Shell-Befehle Dinge adden kann, wo ja die ID auf "8-Stelligkeit" überprüft wird. Muss nun gemäß des Kapselungsprinzips trotzdem in addThing(int id) anstatt in der Shell die ID auf 8-Stelligkeit überprüft werden, weil ja die Klasse mit der LinkedList auch unabhängig von der Shell funzen soll? Oder reicht es, wenn man alle Prüfungen in der Shell durchführt?

4.
Angenommen, ich mache das ID-Attribut der Klasse Thing final, weil es ja unveränderbar ist... muss ich die ID dann public machen (in den Code Conventions hab ich nix gefunden), oder kann ich sie auch private lassen (mit Getter). Gibt es irgendeinen Fall, wo letzteres Sinn macht?
EDIT:
Geht also nur
Java:
public final int ID;
oder auch
Java:
private final int ID; // "ID" oder "id", wenns private ist? Code Conventions sagen da glaub ich nix?
 
Zuletzt bearbeitet:
1. Mehrere returns sind anch modernen Regeln i.O., denn moderne Regeln sagen auch dass die Methoden sehr kurz sein sollen, da geht keine Übersicht verloren. Wenn eine Methode "einen Bildschirm" lang ist odre noch länger ist das etwas anders.
Wenn du eine Map nutzt sieht das spwieso wieder anders aus, muss halt passen zum Anwendungsfall, den kann ich aus 1 Methode nicht erkennen 😉

2. Wenn es die Lesbarkeit erhöht bzw. eindeutigkeit schafft.

3. Für Validierungen gibt es zB. auch den JSR 303. Kompelxere Validierungen die Kontextinformationen benötigen lassen sich nicht in einen Setter/Mutator quetschen. JFace macht das zB. über das DataBinding.

4. Nur "Konstanten" werden groß geschrieben. Eine primitiver Typ wie int ist mit einem final sowas wie eine "Konstante", also wäre ID richtig, das gilt auch für immutable Referenztypen. Ob du das Ding public machst odre per Getter erreichbar hängt eher davon ab, ob es den JavaBeans Konventionen folgen muss, dann Getter, ansonsten public IMHO.
 
Eine Variable ist doch erst eine Konstante, wenn sie
Code:
final
und
Code:
static
ist? Dementsprechend wäre "id" korrekt.
 
Eine Variable ist doch erst eine Konstante, wenn sie
Code:
final
und
Code:
static
ist? Dementsprechend wäre "id" korrekt.

muss nicht unbedingt static sein, eine Konstante kann ihren Wert nicht aendern, was du bei immutable Variablen ja hast

noch zu 1)
ich wuerde mit null returns vorsichtig sein. Je nach Anwendungsfall waere es sinnvoller entweder ein null Objekt (also ein Thing was als leeres Objekt steht) oder eine RuntimeException zu werfen. Null returns zwingen den Benutzer IMMER zu einer Sonderbehandlung

zu 2)
innerhalb einer IDE ist es recht egal, da dort es meist so und so anders dargestellt wird. Ansonsten ist das auf alle Faelle eine geschmackssache. Ich wuerde glaub ich erstmal ueberrascht sein mitten in er Klasse ein this zu sehen

rest siehe maki
 
ich wuerde mit null returns vorsichtig sein. Je nach Anwendungsfall waere es sinnvoller entweder ein null Objekt (also ein Thing was als leeres Objekt steht) oder eine RuntimeException zu werfen. Null returns zwingen den Benutzer IMMER zu einer Sonderbehandlung

Runtime Exceptions sind doch genau so, als ob da ein null zurückkommt. Bis auf die Tatsache, dass wenn diese nicht ordnetlich gefangen wird das gesamte Proggy weghaut. Da muss man sich auch drum kümmern. Und wenn das ein Zustand ist, der nie vorkommen kann, dann kann man auch null zurückgeben, den das kommt ja auch nie vor ;D
 
Du kannst aber dann davon ausgehen, dass du was sinnvolles zurückbekommst wenn die Methode keine Exception wirft.
 
Runtime Exceptions sind doch genau so, als ob da ein null zurückkommt. Bis auf die Tatsache, dass wenn diese nicht ordnetlich gefangen wird das gesamte Proggy weghaut. Da muss man sich auch drum kümmern. Und wenn das ein Zustand ist, der nie vorkommen kann, dann kann man auch null zurückgeben, den das kommt ja auch nie vor ;D

Das haengt halt vom Kontext der Methode ab. Wenn da z.B. ein Wert aus einer Map geholt werden soll, wuerd ich null zurueckliefert, wenn der Wert fehlt. Ist es aber z.B. eine create-Methode in einer Factory, wuerd ich eine entsprechende RuntimeException mit moeglichst sinnvoller Fehlermeldung werfen.
Sehr sinnvoll in diesem Zusammenhang die @Nonnull und @Nullable Annotations (jsr-305 - JSR 305: Annotations for Software Defect Detection in Java - Google Project Hosting).

Zu deiner dritten Frage:
Eventuell koennte es in deinem Fall sinnvoll sein, einfach die Methode addThing zu ueberladen:
Java:
public void addThing(int id) {..}
public void addThing(String id) {
   addThing( validateId( id ) );
}
 
muss nicht unbedingt static sein, eine Konstante kann ihren Wert nicht aendern, was du bei immutable Variablen ja hast
Das hier ergibt doch keinen Sinn:
Java:
private final int id = 42;
Das sollte auf jeden Fall
Code:
static
sein.

In den Code Conventions sind die Konstanten auch nur als
Code:
static
aufgeführt.

… und gerade auch mal in die JLS geschaut: Names
Note that well-designed classes have very few
Code:
public
or
Code:
protected
fields, except for fields that are constants (
Code:
final static
fields)
 
Warum sollte das keinen Sinn ergeben? Die ID eines Objektes wird sich wohl kaum ändern, daher kann man das schon final machen. Und da sich nicht alle Objekte die selbe ID teilen darf die nicht static sein.

EDIT:
die ID sollte dann natürlich nicht für alle Objekte 42 sein, dann kann man die wirklich static machen 😉
 
die ID sollte dann natürlich nicht für alle Objekte 42 sein, dann kann man die wirklich static machen 😉
Darum gings ja. 😉

Wenn sie etwa im Konstruktor mit irgendwelchen Abhängigkeiten gesetzt wird, sieht das natürlich anders aus. Hat für mich dann aber auch nicht das "Feeling" einer Konstante (und ist laut Definition auch keine).
 
Zu 1., da würde ich sehr vorsichtig sein. Besonders bei so Stil sachen haben manche Professoren ihre eigenen Maßstäbe und bei manchen wirst du mit einem break wohl einen Punkteabzug kassieren. Es lässt sich mit der foreach Schleife leider nicht realisieren, aber der "alte" Weg um sich so ein break zu sparen, ist eine boolean einzuführen, in der Schleife jedesmal die boolean zu prüfen und wenn du gefunden hast was du suchst, die boolean umsetzen.
Die von dir gepostete Methode finde ich aber schöner, eleganter und effizienter als das was ich grade beschrieben habe, liegt ganz einfach am technischen Fortschritt.
 
Das hier ergibt doch keinen Sinn:
Java:
private final int id = 42;
Das sollte auf jeden Fall
Code:
static
sein.
ich habe bisher immer eine Konstante als eben eine Variable deren Wert sich nicht aendert gesehen. Somit ist es unabhaengig davon ob static oder nicht. Aber wenn es in der Java welt offiziell ist, dass man nur bei static von Konstanten spricht - ok.
 
Vielen vielen Dank für die Hilfe erstmal 🙂, war sehr informativ.

Werde dann wohl bei 1. die foreach-Schleife mit break benutzen und bei 3. die Überprüfungen in der Shell stehen lassen. Je nachdem, was an der Eingabe nicht stimmt, will ich nämlich verschiedene Fehlermeldungen über die Shell ausgeben und wenn die Fehler in der Eingabe nicht direkt in der Shell erkannt werden, müsste ich lauter Exceptions definieren, die die Shell dann behandelt, anstatt die Shell selbst die Fehler erkennen und Fehlermeldungen ausgeben zu lassen.

Oder verstößt das gegen die Objektorientierung / Datenkapselung, wenn man mit einer public-Methode addThing(int id) einer Klasse theoretisch Dinge mit einer nicht 8-stelligen ID (obwohl sie 8-stellig sein sollte) anlegen könnte, was aber praktisch gesehen nicht geht, da dies in der Shell (aber eben nicht in der Klasse selbst) überprüft wird?
An sich muss man das ja nicht so streng sehen, oder?

EDIT: Das mit return null is glaub ich ne private Methode der Klasse, also kein Problem.
EDIT2: Noch ne Frage, soll man immer alle Getter und Setter und equals() schreiben oder nur, wenn sie auch genutzt werden? Sind ja eh nur 3 Clicks...
 
Zuletzt bearbeitet:
Zum static: Es sind beides Konstante, das static Keyword bewirkt nur, dass die Variable nichtmehr an jede Instanz der Klasse, sondern an die Klasse selber gebunden wird. Hat bei public Variablen noch den Effekt, dass man auch mit MyClass.var auf die Variable zugreifen kann, ohne eine Instanz der Klasse erstellen zu müssen. Eine gute Erklärung dazu hier:
Understanding Instance and Class Members (The Java™ Tutorials > Learning the Java Language > Classes and Objects)

Zu den Getter etc.
Grade im Uni Umfeld würde ich es immer dazu schreiben, bei uns war ein compareTo bzw Spaceship Operator noch das i-Tüpfelchen. Aber man kann sich auch im Allgemeinen die 3 klicks angewöhnen, zB. bei Hibernate oder in Beans werden sie sogar gefordert.
In Java ist der Vorteil von gettern/settern nicht direkt ersichtlich, da man von außen sofort sieht ob direkter Zugriff oder getter, in zB. Ruby kann man es oft von außen nicht sehen.
Allerdings würde ich innerhalb der Klasse auch den direkten zugriff mit getter/setter mischen. Sieh es so, wenn du wirklich nur saubere Daten erwartest, dann direkt, bei allem anderem Setter, dort kann dann auch Validierung etc stattfinden. Allerdings aufpassen, eclipse generiert dir für private Variable public methoden, dass kann natürlich zu ungewünschten Offenlegungen von internen Werten führen.
 
EDIT2: Noch ne Frage, soll man immer alle Getter und Setter und equals() schreiben oder nur, wenn sie auch genutzt werden? Sind ja eh nur 3 Clicks...
JEDER code sollte nur geschrieben werden, wenn es jemand gibt der diesen Code haben will. NIEMALS Code erstellen, ohne dass es jemand gibt der dich dazu zwingt.
 
Zum static: Es sind beides Konstante

Es ist zwar etwas spitzfindig, aber eine klassische Konstante geht nur mit static final, weil nur damit der Wert zur Compile-Zeit festgelegt wird. final-Werte ohne static werden beim Aufruf des Konstruktors zur Laufzeit bestimmt.

Ich würde auf jeden Fall die 2te Lösung ohne das break bevorzugen. Ist kürzer und eleganter 🙂 Je nach dem aus dem return null; ein throw new RuntimeException("fehlerhinweis"); machen.

equals() musst Du nur überschreiben, wenn Du voneinander verschiedene Objekte auf Gleichheit überprüfen willst. Dann sollte man auch unbedingt hashCode() überschreiben. Diese Methoden werden in Collections wie z. B. HashMap oder ArrayList aufgerufen. Wenn in equals() oder hashCode() Fehler sind, kann das fiese Auswirkungen für das Sammeln in Collections geben.
 
1. Ich benutze immer soviele Returns wies gerade passt. Erst eine Variable anzulegen und dann zu prüfen etc. kostet doch nur Performance. Angenommen du hast eine Liste mit 1000 einträgen und machst dann das:
Java:
Thing getThing(int id) {
     Thing found=null;
     for (int i=0;i<this.things.length;i++) {
         if (this.things[i].getId()==id) found=this.things[i];
     }
     return null;
}
Und das 3. Element das gesuchte ist, gehst du durch 997 Elemente komplett umsonst.

2. Ich schreibe grundsätzlich this, evtl. auch weil ich es mir von PHP aus so angewöht habe, dort ist es pflicht. Ist miener Meinung nach auch besser so, weil es klarheit schafft. In einer Methode getId() ist kein Array things definiert, also gibts das auch auch nicht. Stadtdessen aber in der Klasse, also sollte man das auch so schreiben.

EDIT2: Noch ne Frage, soll man immer alle Getter und Setter und equals() schreiben oder nur, wenn sie auch genutzt werden? Sind ja eh nur 3 Clicks...

Getter und Setter besser immer schreiben. Angenommen du schreibst jetzt 2000 Zeilen Code für deine Thing-Klasse und dann fällt dir ein, dass du für die Variable XY eine Überprüfung brauchst, musst du durch 2000 Zeilen Code gehen und das ändern. Mit Getter/Setter änderst dus einmal (und ggf. im Code wenn ne Exception fliegt) und fertig. Ausserdem kanns dann passieren, dass ein Aussenstehender, der deinen Code benutzt, das nicht weis und ungültige werte einschleust.

.equals() bei objekte generell auch.
Java:
String foo1="Hello World"; // "Hello World"
String foo2=((String) "H"+foo1).substring(1); // Zunächst "HHello World" dann "Hello World"
System.out.println(foo1 == foo2 ? "Ja" : "Nein") // Nein
System.out.println(foo1.equals(foo2) ? "Ja" : "Nein"); // Ja

[EDIT]
4. Das wurde schon ziemlich auf den Punkt gebracht. Konstanten sind eben konstant und ändern ihren wert nicht.
Java:
final int SOME_CONSTANT=30;
In anderen Programmiersprachen gibt es das [c]const[/c] keyword. Wenn dann dort steht
Code:
const int SOME_CONSTANT=30;
do_someting(SOME_CONSTANT);
liest der compiler eigentlich
Code:
do_something(30);
 
Zuletzt bearbeitet:
Soweit ich mich errinnere sollte es bei dir "id" heisen und es ist eine Konstante.
static und final sind dann sogenannte Javakonstanten und eben diese werden dann "ID" benannt.

Wie gesagt, kommt jetzt nur so aus dem Hinterstübchen, hab leider keinen beleg zur Hand.
Es macht ja auch Sinn, da du auf "id" mit myThing.getId(); zugreiffst und auf "ID" mit Thing.ID;.

Gruß Vanny

[EDIT]ID ist jetzt vielleicht auch nicht das beste Beispiel aber wenn ich
Java:
myThing.getMAIN_ID();
lese, würd ich schon stutzig werden😉[/EDIT]
 
Zuletzt bearbeitet von einem Moderator:
Ich weiß ja nicht, wieviel Freiraum ihr bei der Aufgabe habt, aber im "richtigen Leben" würde ich für [c]allThings[/c] eine Map und keine Liste nehmen. Hinzufügen wäre dann einfach [c]allThings.put(thing.getID(), thing)[/c] und finden [c]allThings.get(id)[/c]. [c]allThings[/c] könnte z.B. als [c]Map<Integer, Thing> allThings = new HashMap<Integer,Thing>();[/c] definiert sein.
 

Zurück
Oben