CRUD kapseln?

AndiE

Top Contributor
Ich habe da eine Design-Frage. Wenn ich in einem Programm eine Collektion, z.B. eine Liste programmiert habe, dann habe ich die Liste als private-Variable angelegt und die Funktionen zum Erstellen, Löschen, Lesen und Verändern der Elemente( CRUD) als Funktionen, die als Parameter dann die Listenobjekte haben. Beim Lesen verschiedener Beispielprogramme habe ich nun gesehen, dass auch die Liste selbst als Parameter solcher Funktionen benutzt wird, z.B: "public ObjList getList();" oder void setList(ObjList list)". Ich finde, dass dabei die Manipulation der Liste außerhalb des Listenobjektes stattfindet, und habe bisher geglaubt das das dem Prinzip der Kapselung der Funktion widerspricht. Kann mir jemand helfen, das Dilemma aufzulösen?
 

Tobse

Top Contributor
Ich finde, dass dabei die Manipulation der Liste außerhalb des Listenobjektes stattfindet

Das stimmt. Wer dann obj.getList().add() programmiert macht die Kapselung komplett zunichte.

und habe bisher geglaubt das das dem Prinzip der Kapselung der Funktion widerspricht

Auch hierbei gebe ich dir Recht (s.o.). Ich denke man muss sich entscheiden: Entweder übernimmt die Klasse X einen Teil der (oder alle) Verantwortlichkeiten der Liste oder die Verantwortlichkeit der Klasse X beschränkt sich darauf, Zugriff auf die Liste zu geben. Beides gleichzeitig ist IMHO Schwachfug. Stichwort: Separation of Concerns.

P.S.: Es könnte auch sein, dass der Autor der Klasse den Getter&Setter für die Liste nur deshalb eingebaut hat, um dem Getter&Setter Pattern zu entsprechen (was nebenbei imho auch völliger Unsinn ist). Siehe dazu: dieser Blogbeitrag. (Der ist etwas hetzerisch geschrieben und ist von der Logik her auch nicht ganz Schlüssig/Konsequent/Integer; es geht mir um den Grundtenor.)
 

klauskarambulut

Bekanntes Mitglied
Der Code sieht dann im Idealfall so aus
Java:
private List list;
public List getList() {
  return Collections.unmodifieableList(list);
}

public void setList(List list) throws IllegatArgumentException {
  validateList(list);
  this.list.clear();
  this.list.addAll(list);
}

Die Liste kann herausgegeben werden um darüber zu iterieren.
Beim setzen kann ebenfalls validiert werden, wobei das validieren zum Beispiel eine Exception werfen könnte.
Dabei wird dann die alte Liste geleert oder neu erstellt und die Elemente werden in die neue Liste kopiert via addAll oder im Konstruktor der neuen Liste übergeben.

Wichtig, da man sonst das könnte
Java:
List list = new ArrayList();
foo.setList(list);
list.add(new BadThing());
 

Tobse

Top Contributor
Der Code sieht dann im Idealfall so aus
Java:
private List list;
public List getList() {
  return Collections.unmodifieableList(list);
}

public void setList(List list) throws IllegatArgumentException {
  validateList(list);
  this.list.clear();
  this.list.addAll(list);
}

Die Liste kann herausgegeben werden um darüber zu iterieren.
Beim setzen kann ebenfalls validiert werden, wobei das validieren zum Beispiel eine Exception werfen könnte.
Dabei wird dann die alte Liste geleert oder neu erstellt und die Elemente werden in die neue Liste kopiert via addAll oder im Konstruktor der neuen Liste übergeben.

Wichtig, da man sonst das könnte
Java:
List list = new ArrayList();
foo.setList(list);
list.add(new BadThing());

Das ist doch Unsinn, SCNR. Und hier die Gründe:
  1. Die Liste, welche von getList() zurückgegeben wird, ist keine vollwertige Liste, denn sie ist immutable. Dass sich hierbei Fehler einschleichen ist im wahrsten Sinne des Wortes vorprogrammiert.
  2. Die Komplexität der Contracts wird um ein Vielfaches erhöht. Auch hier schleichen sich Fehler schnell ein.
Und überhaupt: der ganze Aufwand nur für das Getter/Setter pattern? IMHO ist ein Decorator anstatt der Getter/Setter hier deutlich einfacher und klärt auch die Verantwortlichkeiten ganz genau (siehe Separation of Concerns).
 

AndiE

Top Contributor
Ich würde das nicht so sehr am Getter/Setter festmachen. Methoden mit Hole/Schreibe dienen genauso dazu private deklarierte Daten öffentlich( public) zugänglich zu machen. Ich habe ehrlich gesagt auch nicht den großen Überblick, um für eine Aufgabenstellung eines der Design Patterns anzuwenden. Welchen Vorteil hat es, sich eine Liste übergeben zu lassen, statt eine Klasse drumrum zu schreiben? Ich denke, es kann für die Lesbarkeit besser sein, wenn ich alle Operationen an einem Datenobjekt in einer Methode habe. Gerade bei Objekten mit vielen Eigenschaften ist es angenehmer, die add-Methode der Collection aufzurufen statt der Schreibe...- Methoden des Collections-Objektes. Aber wie schon gesagt, hängt das sehr vom Anwendungsfall ab.
Ausnahme ist sicher die JEE-Programmierung, wo getter/setter ja sowas wie ein "Interface" sind.
 

mrBrown

Super-Moderator
Mitarbeiter
Ich würde das nicht so sehr am Getter/Setter festmachen. Methoden mit Hole/Schreibe dienen genauso dazu private deklarierte Daten öffentlich( public) zugänglich zu machen.
Was ist für dich der Unterscheid zwischen get/setter und Hole/Schreibe-Methoden, außer, dass das eine deutsch ist?


Welchen Vorteil hat es, sich eine Liste übergeben zu lassen, statt eine Klasse drumrum zu schreiben? Ich denke, es kann für die Lesbarkeit besser sein, wenn ich alle Operationen an einem Datenobjekt in einer Methode habe. Gerade bei Objekten mit vielen Eigenschaften ist es angenehmer, die add-Methode der Collection aufzurufen statt der Schreibe...- Methoden des Collections-Objektes. Aber wie schon gesagt, hängt das sehr vom Anwendungsfall ab.

Liste übergeben oder eine Klasse haben, die die Liste kapselt und zusätzlich Funktionen bereitstellt, sind doch zwei völlig unterschiedliche Dinge.
Ich versteh aber ehrlich gesagt nicht, was du damit überhaupt aussagen willst? Ich verstehe unter add-Methode der Collection das gleiche wie unter Schreibe-Methoden des Collections-Objektes, zumindest wenn Schreiben Daten hinzufügen meint (und nicht sowas wie Writer.write(...))...
 

AndiE

Top Contributor
Ja, tatsächlich sind es zwei unterschiedliche Dinge, eine Listenobjekt zu rückzugeben oder eine Klasse drumrum zu schreiiben. Mir geht es darum, abzuklopfen, wann der Entwickler welches Vorgehen wählen sollte oder könnte. Gehe ich davon aus, dass OOP bedeutet, dass sich Datenobjekte Nachrichen senden, Werte gekapselt werden, Vererbung und Polymorphie dazugehört, komme ich persönlich zu dem Schluss, dass ich private-definierte Eigenschaften public zugänglich machen muss. Da getter und setter sehr allgemein gehalten sind, und die Lesbarkeit des Programmes eher beeinträchtigen, scheint es nach de hier zitierten Text schlau, den Methoden "bildliche Namen" zu geben. Ich sehe keine grundsätzlich andere Möglichkeit diese Aufgabe zu erfüllen, ohne die oben genantnen Prinzipien zu verletzen. Bei so einer Vorgehensweise lege ich um das Listenobjekt eine Klasse und die Methoden bestehen meist aus Vierzeilern. Das ist die eine Art des Vorgehens. Welche Vorteile es haben kann, die Liste direkt zu übergeben, habe ich vorherigen Post versucht zu ergründen. Letztendlich geht es mir eben auch darum, zu verstehen, was "Seperations of Concerns" eben in der Praxis wirklich bedeutet.
 

thecain

Top Contributor
Es kommt darauf an. Habe ich z.b. eine klasse welche eine Liste von Daten statistisch auswertet, dann übergebe ich die ganze Liste auf einmal.

Habe ich eine Klasse, welche ein Lager verwaltet, dann ahbe ich nur eine add Methode und die Liste ist gekapselt.

Bei Designentscheidungen ist es halt oft sie, dass es nicht DIE richtige Lösung gibt, welche man für jeden Fall anwenden kann...
 

Neue Themen


Oben