# Wo bidirektionale Assoziation UNBEDINGT notwendig?



## ernst (31. Jan 2009)

Hallo allerseits,
1)
Meiner Meinung nach ist eine bidirektionale Assoziation _nur_ bei einer GUI unbedingt nötig.
Denn wenn z.B. ein Fenster angeklickt wird, und es dadurch die Farbe ändern soll, muss man vom Listener (Wanze) ja irgendwie zum Fenster kommen. Das geht nur durch einen Verweis.
Ist das richtig?

2) 
Angenommen ein Club hat Stammgäste.
Vom Club kommt man zu einem Stammgast.
Club ---> Gast
Die Navigation von Gast zu Club ist unnötig.
Also reicht eine unidirektionale Assoziation.
Oder gibt es bei solchen Beispielen, die nichts mit GUI zu tun haben, auch eine unbedingte Notwendigkeit einer bidirektionalen Assoziation.


mfg
Ernst


----------



## Templon (31. Jan 2009)

ernst hat gesagt.:
			
		

> Angenommen ein Club hat Stammgäste.
> Vom Club kommt man zu einem Stammgast.
> Club ---> Gast
> Die Navigation von Gast zu Club ist unnötig.
> ...



Kommt halt darauf an, ob der Gast wissen muss in welchem/welchen Lokal/e er Stammgast ist? K.A. ob das irgendwo "zwingend" Notwendig ist.


----------



## ernst (31. Jan 2009)

>
>Kommt halt darauf an, ob der Gast wissen muss in welchem/welchen Lokal/e er 
>Stammgast ist? K.A. ob das irgendwo "zwingend" Notwendig ist.
>
Wenn der Gast wissen muss in welchem/welchen Lokal/e er Stammgast ist, dann muss der Programmierer im Club einen Verweis (Link) auf diesen Gast anlegen.


mfg
Ernst


----------



## maki (31. Jan 2009)

Bidirektionale Ass. sollten vermieden werden falls möglich.


----------



## 0x7F800000 (31. Jan 2009)

ernst hat gesagt.:
			
		

> Wenn der Gast wissen muss in welchem/welchen Lokal/e er Stammgast ist, dann muss der Programmierer im Club einen Verweis (Link) auf diesen Gast anlegen.


da ist doch irgendwo ein knick in der logik :autsch:


----------



## ernst (31. Jan 2009)

Andrey hat gesagt.:
			
		

> ernst hat gesagt.:
> 
> 
> 
> ...


Warum?
In der Klasse Club wird das folgende Attribut eingebaut:
private ArrayList  dieGaeste;
Wenn man dann feststellen will, ob ein Gast zum Club gehört, muss diese Liste durchsucht werden.

mfg
Ernst


----------



## ernst (31. Jan 2009)

maki hat gesagt.:
			
		

> Bidirektionale Ass. sollten vermieden werden falls möglich.


Hast du zu dieser Problematik Quellen (z.B. Links im Internet) bzw. woher hast du diese Info?

mfg
Ernst


----------



## 0x7F800000 (31. Jan 2009)

ernst hat gesagt.:
			
		

> Andrey hat gesagt.:
> 
> 
> 
> ...


Was für tolle listen in der Club-Klasse aufbewahrt werden ist dem Gast absolut wurscht, solange er keine referenz zum Club hat. Er kann auf den Club ja nicht zugreifen (im Unterschied zum abstrakten "man"). Deswegen Knick in der Logik.


----------



## ernst (31. Jan 2009)

Andrey hat gesagt.:
			
		

> Was für tolle listen in der Club-Klasse aufbewahrt werden ist dem Gast absolut wurscht, solange er keine referenz zum Club hat. Er kann auf den Club ja nicht zugreifen (im Unterschied zum abstrakten "man"). Deswegen Knick in der Logik.



I)
"Ein "Gast" kann nicht auf einen "Club" zugreifen".
Das kann man doch aber innerhalb einer _Programmlogik_ realisieren.
Man kann von einem Gast auf einen Club - innerhalb einer Programmlogik - wie folgt zugreifen:

Beispiel:
1) Ein Club -  z.B. myc1 -  wird erzeugt mit einer zugehörigen Gästeliste.

2) Irgendein Gast -  z.B.  myg1 - wird erzeugt (der erst mal gar nichts mit einem Club zu tun hat).

3) Innerhalb eines Programms wird getestet, ob der Gast myg1 (bzw. myg1.name) in der Gästeliste von myc1 vorkommt.
Wenn ja, dann ist ok.
Wenn nein, dann kann myg1 - wenn die Programmlogik dies tun soll -  in die Gästeliste von myc1 aufgenommen werden.

Eine bidirektionale Assoziation ist _nicht_ notwendig!

II) 
Bei meinem Beispiel (Fenster <---> Wanze) íst eine bidirektionale Assoziation dagegen unbedingt notwendig.

III) Zeige mir, warum ich bei meinem Beispiel (Club - Gast) ohne eine bidirektionale Assoziation _nicht_ auskomme, bzw. gib mir ein Gegenbeispiel.

mfg
Ernst


----------



## hdi (31. Jan 2009)

> Bei meinem Beispiel (Fenster <---> Wanze) íst eine bidirektionale Assoziation dagegen unbedingt notwendig.



Verstehe ich nicht. Eine bidirektionale Ass. würde ja nur dann bestehen, wenn das Fenster F einen Listener L kennt, und der Listener L kennt dieses Fenster F.
In dem Falle ist der komplette Listener aber unnötig, weil du von dem Fenster über einen Controller(Listener) wieder zum Fenster kommst. Das Fenster ändert sich also selber, nur über einen unnötigen Umweg. Wieso also sollten also bei einer GUI bidirektionale Ass. zwingend notwendig sein?
Vllt versteh ich dich auch komplett falsch... Aber von Assoziationen spricht man ja eigentlich nur, wenn tatsächlich 
eine abgespeicherte (!) Referenz auf etwas in einer Klasse bestehen muss.


----------



## 0x7F800000 (31. Jan 2009)

die logik wird ja immer besser... -.-

*Aussage 1(falsch):*
"Wenn der Gast wissen muss in welchem/welchen Lokal/e er Stammgast ist, dann muss der Programmierer im Club einen Verweis (Link) auf diesen Gast anlegen."

*Aussage 2(richtig):* 
"In meinem konkreten Programm muss ein gast nichts vom Lokal wissen"

Kannst du mir jetzt bitte erläutern, durch welche Gedankengänge du jetzt von der Aussage 2 zur aussage 1 kommst?
:?:

Ich verstehe überhaupt nicht was dein problem ist. Wenn irgendein hypothetischer Gast in irgendeinem Programm irgendwo eine referenz auf ein Lokal braucht: soll er die referenz doch bekommen, fertig... :roll:


----------



## Gast (31. Jan 2009)

Was ist denn überhaupt eine bidirektionale Assoziation?

Beispiel Liste: Hat ein Element einen Nachfolger, sollte der Nachfolger seinen Vorgänger kennen. Schon hat man eine bidirektionale Assoziation.

Anderes konkretes Beispiel? Eine Firma sollte alle ihre Angestellten kennen. Genauso sollte ein Angestellter aber auch wissen, bei welcher Firma er arbeitet. Wieder bidirektional.


----------



## slawaweis (31. Jan 2009)

ernst hat gesagt.:
			
		

> Hallo allerseits,
> 1)
> Meiner Meinung nach ist eine bidirektionale Assoziation _nur_ bei einer GUI unbedingt nötig.


nein. Bidirektionale Assoziationen sind z.B. unter anderen bei doppelt verkettete Liste notwendig und allen Datenstrukturen, sowie Algorithmen, die darauf aufbauen.

http://de.wikipedia.org/wiki/Liste_(Datenstruktur)#Doppelt_.28mehrfach.29_verkettete_Liste



			
				ernst hat gesagt.:
			
		

> Denn wenn z.B. ein Fenster angeklickt wird, und es dadurch die Farbe ändern soll, muss man vom Listener (Wanze) ja irgendwie zum Fenster kommen. Das geht nur durch einen Verweis.
> Ist das richtig?


ungünstiges Beispiel. Das Event-System in AWT hat was mit der Architektur zu tun. In Java 1.0 wurden die GUI-Events über Funktionen an eine Komponente gemeldet, die man überschreiben musste. Kann man Heute noch nachlesen:

http://java.sun.com/javase/6/docs/api/java/awt/Component.html#mouseDown(java.awt.Event,%20int,%20int)

Ab 1.1 hat man auf Listener umgestellt, einerseits um die Events auch in anderen Klassen verarbeiten zu können, andererseits um einfacher mehr als einen Listener anhängen zu können.

Das man jetzt im oberen Fall eine bidirektionale Assoziation herstellt ist Zufall bzw. hängt es ganz von der Funktionalität ab. Eine andere Funktion beim Klicken wäre z.B. die Farbe in einen anderen Fenster zu ändern, eine Datei mit einem Timestamp zu erstellen oder den Systemwebbrowser zu starten, also alles wo man keine Instanz des geklickten Fensters braucht. Weiterhin wäre für das obere Beispiel keine direkte bidirektionale Assoziation notwenig. Jedes AWTEvent hat die Funktion getSource(), mit der man das Fenster (oder die Komponente) rausbekommt, wo der Listener gerade angeheftet ist. Dann wäre es eine indirekte bidirektionale Assoziation.



			
				ernst hat gesagt.:
			
		

> 2)
> Angenommen ein Club hat Stammgäste.
> Vom Club kommt man zu einem Stammgast.
> Club ---> Gast
> ...


das kommt ganz auf die Funktionalität an. Die Frage ist, können die zwei Objekte eigenständig aktiv werden? Nehmen wir an, der Gast trifft auf der Straße auf einen Freund und der fragt den Gast in welchen Club der geht. Was dann? Anderes Beispiel: es gibt in der Stadt 1000 Clubs und die haben zusammen 1.000.000 unikale Gäste. Jeder Gast kann in mehreren Clubs sein. Nehmen wir auch an, es wird häufig die Information benötigt, in welchen Clubs ein Gast verkehrt. Der Performance wegen wäre es besser eine bidirektionale Assoziation herzustellen, ansonsten müsste man für jede Abfrage alle Clubs und alle Gäste durchgehen. Es kommt also ganz auf die Funktionalität an und nein, man braucht nicht immer eine bidirektionale Assoziation.



			
				maki hat gesagt.:
			
		

> Bidirektionale Ass. sollten vermieden werden falls möglich.


nein, bidirektionale Assoziation sind ein wichtiges Mittel in der Informatik, das man regelmäßig einsetzt. Nur ohne es gut zu kennen, kann man da vieles falsch machen, vor allem in der Thread-Programmierung, Stichwort Deadlock.

Ein gutes Beispiel für unverzichtbare bidirektionale Assoziation in Java ist java.io.File. Wir haben die Funktionen listFiles() und getParentFile(). Ohne einer dieser Funktionen wären die Dateisystemoperationen sehr rechenintensiv.

Slawa


----------



## ernst (1. Feb 2009)

1)


			
				slawaweis hat gesagt.:
			
		

> ernst hat gesagt.:
> 
> 
> 
> ...


Stimmt!
Da hast du mich widerlegt:
Bei dopplet verketteten Listen braucht man unbedingt eine bidirektionale Assoziation!

2)


			
				slawaweis hat gesagt.:
			
		

> Das man jetzt im oberen Fall eine bidirektionale Assoziation herstellt ist Zufall bzw. hängt es ganz von der Funktionalität ab. Eine andere Funktion beim Klicken wäre z.B. die Farbe in einen anderen Fenster zu ändern, eine Datei mit einem Timestamp zu erstellen oder den Systemwebbrowser zu starten, also alles wo man keine Instanz des geklickten Fensters braucht. Weiterhin wäre für das obere Beispiel keine direkte bidirektionale Assoziation notwenig. Jedes AWTEvent hat die Funktion getSource(), mit der man das Fenster (oder die Komponente) rausbekommt, wo der Listener gerade angeheftet ist. Dann wäre es eine indirekte bidirektionale Assoziation.


Stimmt!
Mit dem Gegenbeispiel getSource() hast du mich widerlegt!

Bemerkung:
Was ist - deiner Meinung nach - in diesem Beispiel von mir (Fenster-Listener) besser:
eine bidirektionale Assoziation oder getSource() zu verwenden?

3)


			
				ernst hat gesagt.:
			
		

> Angenommen ein Club hat Stammgäste.
> Vom Club kommt man zu einem Stammgast.
> Club ---> Gast
> Die Navigation von Gast zu Club ist unnötig.
> ...





			
				slawaweis hat gesagt.:
			
		

> das kommt ganz auf die Funktionalität an. Die Frage ist, können die zwei Objekte eigenständig aktiv werden? Nehmen wir an, der Gast trifft auf der Straße auf einen Freund und der fragt den Gast in welchen Club der geht. Was dann?


"eigenständig aktiv". Ich verstehe nicht, wie man "eigenständig aktiv" programmtechnisch umsetzt.
Warum brauche ich da unbedingt _programmtechnisch_  eine bidirektionale Assoziation.
Kannst du mir dazu (zwei eigenständig aktiv) ein möglichst einfaches Beispiel (Quellcode) geben, wo sich eine bidirektionale Assoziation _nicht_ vermeiden lässt?

4)


			
				slawaweis hat gesagt.:
			
		

> Anderes Beispiel: es gibt in der Stadt 1000 Clubs und die haben zusammen 1.000.000 unikale Gäste. Jeder Gast kann in mehreren Clubs sein. Nehmen wir auch an, es wird häufig die Information benötigt, in welchen Clubs ein Gast verkehrt. Der Performance wegen wäre es besser eine bidirektionale Assoziation herzustellen, ansonsten müsste man für jede Abfrage alle Clubs und alle Gäste durchgehen. Es kommt also ganz auf die Funktionalität an und nein, man braucht nicht immer eine bidirektionale Assoziation.


Theoretisch wäre dies auch ohne bidirektionale Assoziation möglich, aber aus Gründen des Laufzeitverhalten ist es besser, eine bidirektionale Assoziation zu benutzen.
Ist das richtig?

5)


			
				slawaweis hat gesagt.:
			
		

> Ein gutes Beispiel für unverzichtbare bidirektionale Assoziation in Java ist java.io.File. Wir haben die Funktionen listFiles() und getParentFile(). Ohne einer dieser Funktionen wären die Dateisystemoperationen sehr rechenintensiv.
> Slawa


Theoretisch wäre dies auch ohne bidirektionale Assoziation möglich, aber aus Gründen des Laufzeitverhalten ist es besser, eine bidirektionale Assoziation zu benutzen.
Ist das richtig?

mfg
Ernst


----------



## ernst (1. Feb 2009)

Andrey hat gesagt.:
			
		

> die logik wird ja immer besser... -.-
> 
> *Aussage 1(falsch):*
> "Wenn der Gast wissen muss in welchem/welchen Lokal/e er Stammgast ist, dann muss der Programmierer im Club einen Verweis (Link) auf diesen Gast anlegen."
> ...



Entschuldigung, dass ich es nicht geschaftt habe, meine Gedankengänge eindeutig und konsistent darzustellen. 
Schaue meine Antwort an slawaweis an.

mfg
Ernst


----------



## 0x7F800000 (1. Feb 2009)

ernst hat gesagt.:
			
		

> Theoretisch wäre dies auch ohne bidirektionale Assoziation möglich, aber aus Gründen des Laufzeitverhalten ist es besser, eine bidirektionale Assoziation zu benutzen.
> Ist das richtig?


Ja, was heißt "theoretisch"... In einem Programm, das an keinerlei Zeitbeschränkungen gebunden ist, und bei dem die Performance vollkommen egal ist, könnte man *überall* nur einfache Assoziationen benutzen. Es würde dann _im prinzip_ auch irgendwie laufen (bzw kriechen). Ein echtzeitsystem mit nur unidirektionalen assoziationen ist dagegen sowohl theoretisch als auch praktisch völliger blödsinn, es geht nicht.

Jetzt mal am beispiel der doppelt verketteten Liste: entferne die bidirektionale assoziation, mache sie zu einer einfach verketteten Liste, und schon kannst du niemals in O(1) auf den Vorgänger zugreifen, sondern brauchst immer O(n).

Genau dasselbe gild für *absolut alle* assoziationen. Man könnte natürlich jede bidirektionale assoziation nur in eine Richtug speichern, und für die andere Richtung jedes mal zig Millionen Objekte und Assoziationen durchlaufen und guggen, ob man da irgendwie von der anderen seite drankommt. Aber dafür würdest du eben niemals O(1) sondern sagen wir mal irgendsowas ebenso inakzeptables wie O(n!^(n^234)) bekommen, auf gut deutsch: der rechner würde verrecken. O(1) in beide Richtungen kriegst du niemals mit unidirektionalen assoziationen hin, weder theoretisch noch sonst irgendwie, und das liegt nicht dran dass es blöd programmiert ist.


----------



## maki (1. Feb 2009)

> nein, bidirektionale Assoziation sind ein wichtiges Mittel in der Informatik, das man regelmäßig einsetzt. Nur ohne es gut zu kennen, kann man da vieles falsch machen, vor allem in der Thread-Programmierung, Stichwort Deadlock.


Bidiretionale Asso. haben eben den Nachteil, dass bei Änderungen 2 Seiten geändert werden müssen um sicherzustellen dass die Daten Konsistent sind.

Einfache Asso. dagegen sind viel einfacher zu warten und zu handhaben.

Wenn man die Wahl hat, sollte man auf bidi. Asso. verzichten und einfache Asso. bevorzugen.

Der Vorschlag des TS die gew. funktionalität über die unidirektionale Asso. zu lösen ist in der Praxis sehr häufig anzutreffen, man legt da mehr Wert drauf den Code wartbar zu halten.

(Doppelt-) verkettete Listen implementieren?
Sorry, aber das sind Hausaufageben für Studenten, im richtigen Leben zahlt einen niemand dafür, zumindest in Java


----------



## Marco13 (1. Feb 2009)

Hab nicht alles gelesen, aber...


			
				ernst hat gesagt.:
			
		

> 1)
> 
> 
> 
> ...



Ich glaube, die "Bidirektionale Assoziation" ist eigentlich NUR dann ein Problem, wenn die Assoziation zwischen Objekte verschiedener Klassen hergestellt wird. Und selbst da kann es Situationen geben, wo das OK ist. Man sollte nur aufpassen, dass das NICHT dazu führt, dass man seine Klassen in dem Sinne spezialisiert, dass sie NUR mit der anderen Klasse zusammenarbeiten können, FALLS sich das vermeiden läßt (was nicht immer der Fall ist).

Oder als Beispiel: Wenn man einen ActionListener hat, den man entweder an einen JButton oder an eine JComboBox hängen will, dann ist eine Assoziation wie

```
class Listener implements ActionListener
{
    private JButton theSource = ...
    public void actionPerformed(ActionEvent e) 
    {
        theSource.setEnabled(false);
    }
}
```
natürlich der Killer: Man könnte "theSource" auch eine JComponent sein lassen, oder schlicht und einfach mit "getSource()" die Quelle des Events bestimmen, und DIE dann disablen - damit wäre der Listener allgemeingültig.

Wenn der Event aber bewirken soll, dass 
1. der Text des Buttons geändert wird
2. ein Item zur ComboBox hinzugefügt wird,
dann braucht man zwangsläufig zwei verschiedene Implementierungen, die dann entweder anhand von getSource().getClass() rausfinden, was zu tun ist, oder direkt ("bidirektional") ihr "Zielobjekt" kennen, falls das nötig ist.


----------



## ernst (1. Feb 2009)

Ich habe dich nicht richtig verstanden.
Deswegen meine folgende Fragen:



			
				Marco13 hat gesagt.:
			
		

> Ich glaube, die "Bidirektionale Assoziation" ist eigentlich NUR dann ein Problem, wenn die Assoziation zwischen Objekte verschiedener Klassen hergestellt wird.


Warum ist das ein Problem?



			
				Marco13 hat gesagt.:
			
		

> Und selbst da kann es Situationen geben, wo das OK ist.


Was meinst du damit?



			
				Marco13 hat gesagt.:
			
		

> Man sollte nur aufpassen, dass das NICHT dazu führt, dass man seine Klassen in dem Sinne spezialisiert, dass sie NUR mit der anderen Klasse zusammenarbeiten können, FALLS sich das vermeiden läßt (was nicht immer der Fall ist).


Den folgenden Satz verstehe ich nicht:
"NUR mit der anderen Klasse zusammenarbeiten können, FALLS sich das vermeiden läßt".
Meinst du vielleicht folgendes:
Wenn man einer Klasse eine Assoziation hinzufügt (durch ein "Assoziationsattribut"), hat man die Klasse spezialisiert. 
Sie kann dann nicht mehr so oft verwendet werden (sie ist nicht mehr so allgemein).
Wenn in meinem Beispiel die Klasse Gast immer mit der Klasse Club assoziert ist, ist sie nicht mehr so allgemein verwendebar, wie wenn sie nicht mit einem Club assoziert ist.



			
				Marco13 hat gesagt.:
			
		

> Oder als Beispiel: Wenn man einen ActionListener hat, den man entweder an einen JButton oder an eine JComboBox hängen will, dann ist eine Assoziation wie
> 
> ```
> class Listener implements ActionListener
> ...


Warum?
Man kann doch oben
private JButton theSource = ... ... getSource() ...
machen und das ist dann flexibel (weil es mehrere Quellen, z.B. JButton, JComponent, usw. geben kann.



			
				Marco13 hat gesagt.:
			
		

> Man könnte "theSource" auch eine JComponent sein lassen, oder schlicht und einfach mit "getSource()" die Quelle des Events bestimmen, und DIE dann disablen - damit wäre der Listener allgemeingültig.


Das meinte ich eben



			
				Marco13 hat gesagt.:
			
		

> Wenn der Event aber bewirken soll, dass
> 1. der Text des Buttons geändert wird
> 2. ein Item zur ComboBox hinzugefügt wird,
> dann braucht man zwangsläufig zwei verschiedene Implementierungen, die dann entweder anhand von getSource().getClass() rausfinden, was zu tun ist, oder direkt ("bidirektional") ihr "Zielobjekt" kennen, falls das nötig ist.


Warum 2 verschiedene Implementierungen. 
Du brauchst doch nur 2 verschiedene Fälle. Die kann man doch aber in einer Methode unterkriegen?


mfg
Ernst


----------



## ernst (1. Feb 2009)

ernst hat gesagt.:
			
		

> Theoretisch wäre dies auch ohne bidirektionale Assoziation möglich, aber aus Gründen des Laufzeitverhalten ist es besser, eine bidirektionale Assoziation zu benutzen.
> Ist das richtig?





			
				Andrey hat gesagt.:
			
		

> Ja, was heißt "theoretisch"... In einem Programm, das an keinerlei Zeitbeschränkungen gebunden ist, und bei dem die Performance vollkommen egal ist, könnte man *überall* nur einfache Assoziationen benutzen. Es würde dann _im prinzip_ auch irgendwie laufen (bzw kriechen). Ein echtzeitsystem mit nur unidirektionalen assoziationen ist dagegen sowohl theoretisch als auch praktisch völliger blödsinn, es geht nicht.
> 
> Jetzt mal am beispiel der doppelt verketteten Liste: entferne die bidirektionale assoziation, mache sie zu einer einfach verketteten Liste, und schon kannst du niemals in O(1) auf den Vorgänger zugreifen, sondern brauchst immer O(n).
> 
> Genau dasselbe gild für *absolut alle* assoziationen. Man könnte natürlich jede bidirektionale assoziation nur in eine Richtug speichern, und für die andere Richtung jedes mal zig Millionen Objekte und Assoziationen durchlaufen und guggen, ob man da irgendwie von der anderen seite drankommt. Aber dafür würdest du eben niemals O(1) sondern sagen wir mal irgendsowas ebenso inakzeptables wie O(n!^(n^234)) bekommen, auf gut deutsch: der rechner würde verrecken. O(1) in beide Richtungen kriegst du niemals mit unidirektionalen assoziationen hin, weder theoretisch noch sonst irgendwie, und das liegt nicht dran dass es blöd programmiert ist.


Danke für deinen *hervorragenden* Beitrag. Er hat mir anschaulich erklärt, worum es geht.

Ich fasse nochmals zusammen:

Vorteil (bidirektionale Assoziation gegenüber unidirektionaler Assoziation)
besseres Laufzeitverhalten (Performance)

Nachteil:
Bei Änderungen (z.B. wechselt ein Gast den Club) müssen 2 Seiten geändert werden müssen,  um sicherzustellen dass die Daten konsistent sind.
Unidirektionale Assoziationen sind dagegen viel einfacher zu warten.

Fragen:
1)
Weisst du noch ein paar weitere Punkte für Vor- und Nachteile?

2)
Wann soll man deiner Meinung nach unidirektionale Assoziationen, wann bidirektionale Assoziation verwenden?

mfg
Ernst


----------



## 0x7F800000 (1. Feb 2009)

ernst hat gesagt.:
			
		

> Danke für deinen *hervorragenden* Beitrag. Er hat mir anschaulich erklärt, worum es geht.


Bwoah, das ist ein recht außergewöhnliches Kompliment, normalerweise bin ich eher schlecht, was das erklären von allgemeinen sachen angeht, thx^^



> Vorteil (bidirektionale Assoziation gegenüber unidirektionaler Assoziation)
> besseres Laufzeitverhalten (Performance)


ja, wenn du die verbindungen auch benutzst, versteht sich... Wenn man einfach so unnötige Referenzen reinpflanzt, sollte das die performance imho senken. Zumindest hat der GC schonmal mehr zu arbeiten, und das ist auch nicht kostenlos => immer so viel wie nötig, so wenig wie möglich und alles in Maßen...



> Nachteil:
> Bei Änderungen (z.B. wechselt ein Gast den Club) müssen 2 Seiten geändert werden müssen,  um sicherzustellen dass die Daten konsistent sind.
> Unidirektionale Assoziationen sind dagegen viel einfacher zu warten.


jo, aber da gewöhnt man sich dran. Wenn man weiß was man haben will, dann schafft man es meistens, da keine Fehler einzubauen.



> 1) Weisst du noch ein paar weitere Punkte für Vor- und Nachteile?


Die Referenzen kosten Speicherplatz. GC muss mehr absuchen (wie oben erwähnt).
Aber wenn man O(1) Zugriff braucht, kommt man da einfach nicht drumherum.



> 2)
> Wann soll man deiner Meinung nach unidirektionale Assoziationen, wann bidirektionale Assoziation verwenden?


Dazu kann es keine "Meinung" geben. Man formuliert das Problem, stellt die Forderungen, hockt sich hin und fängt an verschiedene Szenarien durchzurechnen. Die Struktur, bei der am ende die kleinste laufzeit rauskommt, hat gewonnen. Man berücksichtigt dabei das geforderte Laufzeitverhalten für kleine und große Datenmengen, passt auf, dass es nicht zu speicherfressend ist usw.
Darüber gibts tolle Vorlesungen und viele dicke Bücher, kannst mal in eine Bibliothek gehen, und nach irgendwelchen Büchern suchen wo zB. "Datenstrukturen" draufsteht, da werden die klassischen Sachen erklärt (Listen, Bäume, Octrees, BSP's, Mengen, Maps, HashMaps, Skiplists, Graphen) und der ganze Zeug. Für die Spezialisierteren Strukturen, wo viele eigenbau-objekte mitmischen sollte man dann zu irgendwelchen "Softwareentwicklung"-Büchern greifen und guggen, dass da ein paar schöne uml-diagramme drin sind, und ein paar Beispiele gegeben werden, wie wo was verknüpft wird. Dann läuft man wieder los, und sucht nach irgendeinem Buch über verschiedene Design-Patterns, schaut sich an, wie wo was da verknüpft ist.
Wenn man gerafft hat, wie die eigenen Programme innendrin funktionieren, wenn man die Abläufe nachvollziehen kann, dann kriegt man das richtige Gefühl, und kann dass einfach so aus dem Bauch heraus alles richtig konstruieren, und greift dann immer seltener zu irgendwelchen dicken Büchern und Rechnungen.


----------



## slawaweis (1. Feb 2009)

@ernst
ich glaube inzwischen verstanden zu haben, wo dein ursprüngliches Problem liegt. Ich versuche es also noch mal.



			
				ernst hat gesagt.:
			
		

> 1)
> Meiner Meinung nach ist eine bidirektionale Assoziation _nur_ bei einer GUI unbedingt nötig.
> Denn wenn z.B. ein Fenster angeklickt wird, und es dadurch die Farbe ändern soll, muss man vom Listener (Wanze) ja irgendwie zum Fenster kommen. Das geht nur durch einen Verweis.
> Ist das richtig?


Du verwechselst hier bidirektionale Assoziationen mit dem MVC-Pattern:

http://de.wikipedia.org/wiki/Model_View_Controller

auf diesem Pattern sind großenteils AWT und Swing aufgebaut und es ist auch gut so, den es ist ein sehr klares und durchgehend konsistentes Pattern. Im Endeffekt ist es keine bidirektionale Assoziation, sondern eine Dreiecksbeziehung. In dieser Beziehung können bidirektionale Assoziationen aufgebaut werden, müssen aber nicht. Der Kern ist (wie auch auf der Grafik in dem Wikipedia-Artikel dargestellt), dass der Controller das View und das Model kennt, weitere Verknüpfungen hängen ganz von der notwendigen Funktionalität ab.



			
				maki hat gesagt.:
			
		

> > nein, bidirektionale Assoziation sind ein wichtiges Mittel in der Informatik, das man regelmäßig einsetzt. Nur ohne es gut zu kennen, kann man da vieles falsch machen, vor allem in der Thread-Programmierung, Stichwort Deadlock.
> 
> 
> Bidiretionale Asso. haben eben den Nachteil, dass bei Änderungen 2 Seiten geändert werden müssen um sicherzustellen dass die Daten Konsistent sind.
> ...


kannst Du mir bitte sagen, aus welchen Buch das Ganze ist oder wer es Dir so beigebracht hat? Besonders das mit den unidirektionalen Assoziationen und der Praxis. Wer übrigens denkt, doppelt verkettete Listen zu implementieren sei einzig und alleine dazu da Studenten Hausaufgaben zu verschaffen, der hat dann alles gründlich missverstanden.



			
				ernst hat gesagt.:
			
		

> Ich fasse nochmals zusammen:
> 
> Vorteil (bidirektionale Assoziation gegenüber unidirektionaler Assoziation)
> besseres Laufzeitverhalten (Performance)
> ...


Du verbeißt dich hier in Details, ohne das Gesamtbild zu betrachten. *Es ist vollkommen egal*, welche Vorteile oder Nachteile die beiden Ansätze haben. Das worauf es ankommt, ist *die Funktionalität oder die Anforderung*. In der Praxis hat man eine Reihe von Bedienungen, die ein Programm oder Implementierung erfühlen muss. Beispiel: nehmen wir an, man hätte uns so eine Spezifikation gegeben (die nicht mehr verhandelbar ist):


```
public interface Clubs
 {
 // listet alle Clubs im System auf
 public List<Club> listClubs();
 }

public interface Club
 {
 // listet alle Gäste in einem Club auf
 public List<Guest> listGuests();
 }

public interface Guest
 {
 // listet alle Clubs auf, zu diesem Gast gehört
 public List<Club> listClubs();
 }
```

dazu gibt es eine Reihe von Operationen die damit gemacht werden, die Angabe der Daten, die damit gespeichert werden, und die Rahmenbedienungen für die Ausführung, z.B. Geschwindigkeit, Sicherheit, Wartbarkeit, Dokumentation. Nun muss man es implementieren und die Korrektheit beweisen. Auch wenn die Interfaces bidirektionale Assoziationen suggerieren, man könnte die obere Spezifikation sowohl bidirektional als auch unidirektional implementieren. Wenn beide Implementationen die restlichen Bedienungen erfühlen, dann sind die beiden gleich gültig und man kann eine Münze verwerfen, welche denn nun genommen wird.

Was die Wartung angeht, so ist es sehr kurzsichtig zu sagen, das unidirektionale Assoziationen pauschal weniger Wartung erfordern. Wie immer kommt es hier auf das zu lösende Problem an. Es könnte sein, dass der Verzicht auf bidirektionale Assoziationen in einem Teil, wo die angebracht wären, mehr Pflegeaufwand hinter sich ziehen würde, weil man mehr Code bräuchte um das Problem zu lösen. Wieder hier das Beispiel mit java.io.File. Schmeißt fiktiv die Funktion getParentFile() aus der Klasse raus und überlegt, welche Umwege man schreiben und warten müsste, um die Funktion zu substituieren.

Slawa


----------



## maki (1. Feb 2009)

> kannst Du mir bitte sagen, aus welchen Buch das Ganze ist oder wer es Dir so beigebracht hat? Besonders das mit den unidirektionalen Assoziationen und der Praxis.


Vergleiche doch einfach mal die Setter Methoden der beiden Implementierungen, 2 Methoden anstatt eine, Daten werden in 2 Orten (Objekten) gehalten welche dann  auch noch gleich sein müssen.
In der Praxis nutzt man dann nur eine Setter Methode welche die andere Setter Methode implizit aufruft.



> Wer übrigens denkt, doppelt verkettete Listen zu implementieren sei einzig und alleine dazu da Studenten Hausaufgaben zu verschaffen, der hat dann alles gründlich missverstanden.


Ach ja?
Dann sag mir soch bitte wer heute noch dafür zahlt dass jemand grundlegende  (simple) Datenstrukturen implementiert welche Stundenten im ersten Semester erlernen?


----------



## 0x7F800000 (1. Feb 2009)

maki hat gesagt.:
			
		

> Ach ja?
> Dann sag mir soch bitte wer heute noch dafür zahlt dass jemand grundlegende  (simple) Datenstrukturen implementiert welche Stundenten im ersten Semester erlernen?


Das ist dieselbe zahlende Kundschaft, die den erstsemestlern ihre Datenschredder und sonstige kleine tools abkauft


----------



## Marco13 (2. Feb 2009)

Ohje - aktives Zitatleichenfleddern :roll: Ich betreib' dann mal Kontextzerstörung...

_Warum ist das ein Problem?_
Weil es weniger Flexibilität und höheren Verwaltungsaufand bewirken kann.

_Was meinst du damit?_
Dass sich Bidirektionale Assoziationen manchmal nicht (mit vertretbarem Aufwand) vermeiden lassen.

_Meinst du vielleicht folgendes:
Wenn man einer Klasse eine Assoziation hinzufügt (durch ein "Assoziationsattribut"), hat man die Klasse spezialisiert. 
Sie kann dann nicht mehr so oft verwendet werden (sie ist nicht mehr so allgemein).
_
Jup.

_Warum 2 verschiedene Implementierungen. 
Du brauchst doch nur 2 verschiedene Fälle. Die kann man doch aber in einer Methode unterkriegen?_
Ja. Und wenn man noch einen dritten oder zwölften oder zweiundvierzigeinhalbten Fall braucht, kann man das auch in einer Methode machen. Muss man aber nicht.


----------



## ernst (2. Feb 2009)

slawaweis hat gesagt.:
			
		

> Ab 1.1 hat man auf Listener umgestellt, einerseits um die Events auch in anderen Klassen verarbeiten zu können, andererseits um einfacher mehr als einen Listener anhängen zu können.
> 
> Das man jetzt im oberen Fall eine bidirektionale Assoziation herstellt ist Zufall bzw. hängt es ganz von der Funktionalität ab. Eine andere Funktion beim Klicken wäre z.B. die Farbe in einen anderen Fenster zu ändern, eine Datei mit einem Timestamp zu erstellen oder den Systemwebbrowser zu starten, also alles wo man keine Instanz des geklickten Fensters braucht. Weiterhin wäre für das obere Beispiel keine direkte bidirektionale Assoziation notwenig. Jedes AWTEvent hat die Funktion getSource(), mit der man das Fenster (oder die Komponente) rausbekommt, wo der Listener gerade angeheftet ist. Dann wäre es eine indirekte bidirektionale Assoziation.



Angenommen man hat die Klasse MyFenster, in der man einen Button reinmacht und an diesen einen Listener anbringt.
Mit getSource kommt man zwar an den Button.
Wie kommt man aber (evtl. von dem Button) an die Instanz der Klasse MyFenster?
Das ist mir nicht klar.
Kann mir jemand evtl. einen einfachen, kurzen Quellcode dazu bringen?

PS:
public void actionPerformed (ActionEvent myae) {
// Damit kommt man zum Button
...myae.getSource()....
}


mfg
Ernst


----------



## Marco13 (2. Feb 2009)

Theoreitsch kann man sich mit button.getParent().getParent()......getParent() immer weiter hochhangeln (dafür gibt's auch eine vorgefertigte Methode aus SwingUtilities), aber das wäre natürlich gröbster Unfug.


----------



## Puste (2. Feb 2009)

maki hat gesagt.:
			
		

> Der Vorschlag des TS die gew. funktionalität über die unidirektionale Asso. zu lösen ist in der Praxis sehr häufig anzutreffen, man legt da mehr Wert drauf den Code wartbar zu halten.



In der Praxis habe ich seltenst die Möglichkeit von einer beliebigen Codezeile Methoden eines beliebigen Objektes aufzurufen. Am Beispiel Gast - Club erklärt: Ich schreibe einen Controller der ein Gast Objekt behandelt. Will nun, dass der Gast den Club verlässt und was muss ich machen? Mir über irgendein Singelton das Clubobjekt holen, oder aber das Clubobjekt irgendwie an meinen Controller (durch zig weitere Instanzen) weiterreichen, um in diesem Club den Gast zu entfernen.
Anderstrum, würde der Gast den entsprechenden Club referenzieren, dann hätte ich das gleiche Problem, wenn ich per Controller an den Gästen eines Clubs rumwerkeln will.

Die Logik wird hier zu gunsten eines einfacheren Models verkompliziert.

Auf gut deutsch gesagt:
Da scheiss ich lieber auf das bissl mehr Komplexität im Model, welches sonst auch von Grundschülern entwickelt werden könnte und halte lieber die Architektur, das globale Design sauber und einfach.


----------



## Puste (3. Feb 2009)

Ich verstehe ebenso nicht was an folgendem so kompliziert sein soll:

Im Club:

```
public void addGuest(Guest g) {
    this.liste.add(g);
    if(g.getClub() != this)
        g.setClub(this);
}
```

Im Gast:

```
public void setClub(Club c) {
    this.club = c;
    if(!c.containsGuest(this))
        c.addGuest(this);
}
```


----------



## maki (3. Feb 2009)

> Die Logik wird hier zu gunsten eines einfacheren Models verkompliziert.


Das Model ist das wichtigste, soz. das "globale Design", solange das Model genauso aussagekräftig ist, hält man es einfacher.



> Ich verstehe ebenso nicht was an folgendem so kompliziert sein soll:


Kompliziert? Nö, aber komplizierter,
Abgesehen von der unschönen Redundanz in deinem Code...

Dann vergleiche doch mal hiermit:

```
public void addGuest(Guest g) {
    this.liste.add(g);
}
```


Du aber hast nun 2 Methoden, welche symetrisch funktionieren müssen, da es sonst zu inkonsistenten kommen wird.
Immer wenn du eine anpasst, *musst* du auch die andere anpassen.
Dasselbe gilt dann übrigens auch für deine remove Methode*n*.
Wenn du dann auf Exceptions reagieren musst wird dass ganz kompliziert, schliesslich willst du ja deine Objekte nicht in einem inkonsistenten Zustand hinterlassen.

In der Praxis würde man dass übrigens _eher_ so umsetzen, wenn es denn sein muss..

```
public void addGuest(Guest g) {
    this.liste.add(g);
    if(g.getClub() != this)
        g.setClub(this);
}
```


```
public void setClub(Club c) {
    this.club = c;
}
```
bzw. umgekehrt und sich darauf einigen, dass immer nur eine Methode zum adden aufgerufen wird.

Bei ein paar Hunderttausend (bzw. Millionen)Zeilen Code achtet man sehr wohl darauf die Dinge einfach zu halten, denn im Stress wird häufig etwas übersehen und kompliziert werden die Dinge ganz von selbst.


----------



## Puste (3. Feb 2009)

maki hat gesagt.:
			
		

> Du aber hast nun 2 Methoden, welche symetrisch funktionieren müssen, da es sonst zu inkonsistenten kommen wird.
> Immer wenn du eine anpasst, *musst* du auch die andere anpassen.
> Dasselbe gilt dann übrigens auch für deine remove Methode*n*.
> Wenn du dann auf Exceptions reagieren musst wird dass ganz kompliziert, schliesslich willst du ja deine Objekte nicht in einem inkonsistenten Zustand hinterlassen.



Wie gesagt, lieber das wenige an Komplexität im Model anstatt die Architektur zu verhuddeln.
Natürlich ist es kompliziert*er*, dass habe ich doch selbst geschrieben und das braucht man mir nicht weiter erklären. Es wäre weitaus sinnvoller gewesen zu erklären wie man die beschriebenen Probleme der Alternative unidirektionale Verbindung lösen könnte. Oder warum diese nicht weitaus tragischer ist.




			
				maki hat gesagt.:
			
		

> In der Praxis würde man dass übrigens eher so umsetzen, wenn es denn sein muss..
> + Code


Davon halte ich rein garnichts. Das Model ist durch den angegebenen Code nach außen inkonsistent und ohne Kenntnisse der Implementation rennt man in die Falle. Wer ein Model entwickelt, was nach außen nicht konsistent ist, der macht meiner Meinung nach was falsch. Ebenso wie derjenige, der eine Klasse entwickelt, dessen Implementierung man zwingend kennen muss um, um nicht in die erstbeste Falle zu tappen.


Und natürlich stimme ich auch zu, wenn man bei den üblichen Tonnen code Komplexität einsparen will. Aber scheinbar diskutieren wir hier ja nicht darüber welche Vor- und Nachteile die beiden Varianten haben, sondern nur über die Nachteile der einen Seite.

Und somit kann ich auf Deinen Post nur antworten mit:
Jap, natürlich sind bidirektionale Assoziationen komplizierter, richtig erkannt!


----------



## maki (3. Feb 2009)

> Wie gesagt, lieber das wenige an Komplexität im Model anstatt die Architektur zu verhuddeln.


Wie gesagt, bei deiner "Lösung" ist beides (und mehr) schlecht gelöst, ob du das verstehst oder nicht ist mir egal, da du offenbar Beratungsresistent bist und nicht mal auf meine Argumente eingehst


----------



## Puste (3. Feb 2009)

maki hat gesagt.:
			
		

> Wie gesagt, bei deiner "Lösung" ist beides (und mehr) schlecht gelöst, ob du das verstehst oder nicht ist mir egal, da du offenbar Beratungsresistent bist und nicht mal auf meine Argumente eingehst



Ich entschuldige mich, Du scheinst diesen Thread aber auch sehr persönlich zu nehmen. Daher nun auch eine persönliche Antwort. Ich bin doch auf Deine Argumente eingegangen, habe Dir zugestimmt, dass eine bidirektionale Assoziation komplizierter ist als eine unidirektionale. Das ist bisher Deine Argumentation zu meinem Beitrag hier. Oder habe ich da etwas überlesen?

Im letzten Beitrag habe ich lediglich versucht darauf hinzuweisen, dass nicht auf die Nachteile der anderen Variante, welche ich beschrieben habe, eingegangen wurde, sondern nur auf die Nachteile der bidirektionalen Assoziation.


Ich kann die Problemstellung auch gerne nochmal ausführlicher darlegen:
Nehmen wir wieder das Club - Gast Beispiel, ich sitze nun vor einer Methode, welche durch sagen wir 5 andere Methoden verschachtelt aufgerufen wird. (Also kein direkter Aufruf dieser Methode.) Dies kommt bei etwas komplexeren Programmen recht häufig vor. Die Methode bekommt nun den Gast übergeben und muss den Gast aus dem entsprechenden Club entfernen. Wie mache ich dies ohne ein Singleton anzusprechen oder alle aufrufenden Methoden dahingehend abzuändern, dass diese das Clubobjekt durchreichen?


----------



## byte (3. Feb 2009)

Ich sehs genauso wie Maki. Solange es geht, sollte man auf bidirektionale Referenzierung verzichten. Wenn mans nicht zwingend braucht, macht sowas das Modell nur unnötig komplex. Viele Implementierungsdetails werden durch bidirektionale Referenzierung komplizierter, ich denke dabei an die Implementierung von equals(), hashcode(), compareTo(), sowie das Anlegen von Deep Copies, Serialisierung in proprietäre Formate oder Datenbank-Mappings. 

Das alles wird wesentlich aufwändiger, wenn man unnötig viele bidirektionale Referenzen baut. Leider erlebt man es ziemlich häufig, dass in einem frühen Entwicklungsstadium viele unnötige (bidirektionale) Referenzen gebaut werden. Man weiss noch nicht genau, was man alles braucht, also macht man lieber zuviele Referenzen als zu wenig. Und am Ende kommt dann ein riesiges Kneul als Objektgraph raus.

Sicherlich gibt es viele Anwendungsfälle, wo man bidirektionale Verbindungen braucht. Aber anfangen würde ich beim Entwurf immer mit unidirektionalen Verbindungen und - wenns gar nicht anders geht - macht man die dann später bei Bedarf bidirektional.


----------



## maki (3. Feb 2009)

> Oder habe ich da etwas überlesen?


Aus meinem vorletztem Beitrag


> Abgesehen von der unschönen Redundanz in deinem Code...





> Du aber hast nun 2 Methoden, welche symetrisch funktionieren müssen, da es sonst zu inkonsistenten kommen wird.
> Immer wenn du eine anpasst, musst du auch die andere anpassen.
> Dasselbe gilt dann übrigens auch für deine remove Methoden.
> Wenn du dann auf Exceptions reagieren musst wird dass ganz kompliziert, schliesslich willst du ja deine Objekte nicht in einem inkonsistenten Zustand hinterlassen.





> bzw. umgekehrt und sich darauf einigen, dass immer nur eine Methode zum adden aufgerufen wird.


Code habe ich mal weggelassen.

Martin Folwer hat übrigens auch drüber geschrieben: http://sourcemaking.com/refactoring/change-bidirectional-association-to-unidirectional

Persönlich?
Nee, aber wenn man nicht auf Argumente eingeht sondern dem anderen ein schlechtes Design/Architektur vorwirft ohne dies zu hinterlegen, ist eine Diskussion überflüssig.



> Im Club:
> 
> ```
> public void addGuest(Guest g) {
> ...


[/quote]




> dass nicht auf die Nachteile der anderen Variante, welche ich beschrieben habe, eingegangen wurde


Diese hier?


> In der Praxis habe ich seltenst die Möglichkeit von einer beliebigen Codezeile Methoden eines beliebigen Objektes aufzurufen. Am Beispiel Gast - Club erklärt: Ich schreibe einen Controller der ein Gast Objekt behandelt. Will nun, dass der Gast den Club verlässt und was muss ich machen? Mir über irgendein Singelton das Clubobjekt holen, oder aber das Clubobjekt irgendwie an meinen Controller (durch zig weitere Instanzen) weiterreichen, um in diesem Club den Gast zu entfernen.
> Anderstrum, würde der Gast den entsprechenden Club referenzieren, dann hätte ich das gleiche Problem, wenn ich per Controller an den Gästen eines Clubs rumwerkeln will.


*sichergehen will dass ich auf das richtige Antworte*


----------



## ernst (4. Feb 2009)

> kannst Du mir bitte sagen, aus welchen Buch das Ganze ist oder wer es Dir so beigebracht hat? Besonders das mit den unidirektionalen Assoziationen und der Praxis.





			
				maki hat gesagt.:
			
		

> Vergleiche doch einfach mal die Setter Methoden der beiden Implementierungen, 2 Methoden anstatt eine, Daten werden in 2 Orten (Objekten) gehalten welche dann  auch noch gleich sein müssen.
> In der Praxis nutzt man dann nur eine Setter Methode welche die andere Setter Methode implizit aufruft.


Mir ist das leider etwas zu abstrakt (ich kann mir das nicht vorstellen, was du meinst).
Zum Verständnis:
Kannst du das an dem Beispiel Club -- Gast zeigen (möglichst einfacher Quellcode).
Einmal im bidirektionalen Fall und das andere Mal im unidirektionalen Fall?
Dann sehe ich den konkreten Unterschied.


mfg
Ernst


----------



## ernst (4. Feb 2009)

@slawaweis


			
				slawaweis hat gesagt.:
			
		

> dazu gibt es eine Reihe von Operationen die damit gemacht werden, die Angabe der Daten, die damit gespeichert werden, und die Rahmenbedienungen für die Ausführung, z.B. Geschwindigkeit, Sicherheit, Wartbarkeit, Dokumentation. Nun muss man es implementieren und die Korrektheit beweisen.


Die Korrektheit beweisen?
Mit Hoare-Kalkül oder ähnlichem?
Geht das überhaupt bei objektorientieretn Sprachen?

Ich weiß, das gehört zwar nicht gerade hierher ....

mfg
Ernst


----------

