Objektbeziehungen - Assoziationen

https://bildung.social/@oerinformatik/110425552692026065

https://oer-informatik.de/uml-klassendiagramm-assoziation

tl/dr; (ca. 10 min Lesezeit): Mit einzelnen Objekten erreicht man in der OOP noch nicht viel. Erst im Zusammenspiel als Objektsammlungen und über Objektinteraktionen entwickelt die OOP ihre Stärken. Der folgende Text behandelt die Fragen: Welche Objektbeziehungen gibt es? Was unterscheidet Assoziation, Aggregation und Komposition? Versuch der Klärung. (Zuletzt geändert am 24.05.2023)

Dieser Text ist ein Teil der Infotext-Serie zu UML-Klassendiagrammen:

Im Gegensatz zur Vererbung, die Beziehungen zwischen Klassen darstellt, beschreiben Assoziationen Beziehungen zwischen Objekten. Objektbeziehungen können zur Laufzeit eines Programms geändert werden.

Assoziation

Beziehungen zwischen Objekten nennt man Assoziation, wenn ein Objekt eine Methode eines anderen Objekts aufruft (oder ein Attribut eines anderen Objekts ändert). Beide Objekte arbeiten für ein gemeinsames Ziel zusammen. Die Beziehungen werden im UML Klassendiagramm durch eine Linie zwischen den Objekten dargestellt:

Assoziation zwischen Kunde und Rechnung
Assoziation zwischen Kunde und Rechnung

Das gemeinsame Ziel der beteiligten Objekte wird oft anhand des Assoziationsnamens klar. Phrasen wie “benutzt ein”, “ist zugeordnet zu” oder “hat eine Beziehung zu” können diese Assoziationen recht unkonkret beschreiben, häufig finden sich präzisere Namen der Beziehung. Diese Assoziationsnamen werden auf der Assoziationskante mit Pfeil in Leserichtung angegeben: ein/e Kund*in bezahlt eine Rechnung.

Assoziation: ein Kunde bezahlt eine Rechnung
Assoziation: ein Kunde bezahlt eine Rechnung

In einer Assoziation wird aber keine über das eigentliche Ziel hinaus gehende Abhängigkeit beschrieben. Wenn die Rechnung bezahlt ist, haben Kund*in und Rechnung also ggf. keine weitere Beziehung zueinander. Anhand einer konkreteren Darstellung soll die temporäre Zusammenarbeit beider Objekte deutlich werden:

Eine Kund*in bezahlt eine Rechnung: konkretere Darstellung
Eine Kund*in bezahlt eine Rechnung: konkretere Darstellung

Das Ziel der Zusammenarbeit ist das Bezahlen einer Rechnung. Dazu erhält ein Kundenobjekt bei Aufruf der Methode bezahleRechnung() ein Rechnungsobjekt übergeben. Die Kund*in nutzt die Rechnung-Methoden getRechnungsbetrag() und setBezahlt(), um die Rechnung von seinem vermoegen zu bezahlen. Nach Abschluss dieser Transaktion hält der Kunde keine Referenz mehr auf die Rechnung - beide Objekte können unabhängig voneinander fortbestehen.

Gerichtete Assoziation

Bei den bisherigen Darstellungen wurde im UML-Diagramm offengelassen, ob beide Objekte einander kennen. Beide Enden der Assoziationskante waren unspezifiziert.

Wenn genauer festgelegt werden soll, welches Objekt vom anderen weiß - also Zugriff auf eine Referenz hat - kann dies mit Kantenenden spezifisch dargestellt werden:

Unidirektional navigierbare Assoziationen

unidirektionale Navigation von Kunde zu Rechnung
unidirektionale Navigation von Kunde zu Rechnung

Ein Kunde kann auf die Rechnung zugreifen (Pfeil in Richtung Rechnung), eine Rechnung weiß jedoch nicht, welche Kund*in sie bezahlt hat (Kreuz auf der Kantenseite Kunde). Eine Rechnung hält also keine Referenz auf Kunde. Das wird v.a. auch im Beispielcode deutlich:

Da diese Assoziation nur in einer Richtung navigiert werden kann, spricht man von einer unidirektional navigierbaren Assoziation.

Bidirektional navigierbare Assoziation

Wird beispielsweise beim Bezahlvorgang aufseiten der Rechnung vermerkt, welche Kund*in die Rechnung bezahlt hat (z.B. in der Methode setBezahlt(istBezahlt:boolean, kunde:Kunde)), so kennen sich beide Objekte. Die Assoziation kann in beide Richtungen navigiert werden (Kunde ruft Methoden von Rechnung auf und umgekehrt). Wir sprechen dann von einer bidirektionale navigierbaren Assoziation. In der UML wird dies durch zwei Pfeilenden an der Assoziationskante gekennzeichnet:

Kunde kennt Rechnung, Rechnung kennt Kunde
Kunde kennt Rechnung, Rechnung kennt Kunde

Im Beispielcode wird der gegenseitige Methodenaufruf der beiden Klassen deutlich:

Nicht navigierbare Assoziation

Modellierbar ist auch der Fall, dass keine Richtung navigierbar ist. Keines der Objekte hält dann direkt Referenzen auf das jeweils andere (zwei “x” an den Kantenenden), es besteht nur eine logische, aber keine faktische Beziehung. Ich bin für jeden Hinweis dankbar, wann es einen sinnvollen Anwendungsfall hierfür gibt und wie dieser implementiert werden muss.

Multiplizitäten

Bei den bisherigen Notationsmitteln für Assoziationen wurde noch keine Festlegung getroffen, wie viele Objekte der einen Klasse mit wie vielen Objekte der anderen Klasse in Beziehung stehen. Im folgenden Diagramm sind eine Reihe von Festlegungen für ein Programm zur Erstellung von Rechnungen getroffen. Zu jeder Kante können zwei unterschiedliche Multiplizitäten ausgewiesen werden: für jeder der beiden Klassen je eine:

Rechnung, Rechnungsanschrift, Lieferanschrift, Rechnungsposition und Steuersatz
Rechnung, Rechnungsanschrift, Lieferanschrift, Rechnungsposition und Steuersatz

Sofern die Multiplizität die Zahl 1 überschreitet, werden die entsprechenden Referenzen in Objektsammlungen gespeichert. Je nach Programmiersprache können diese als Array, ArrayList, List o.ä. umgesetzt werden. Jede Multiplizität legt fest, wie viele Instanzen der Klasse, an der sie notiert ist, an der Beziehung mindestens und höchstens beteiligt sind. Möglich sind folgende Notationen:

UML-Notation
am Kantenende
Bedeutung Beispiel
n oder
n..n

(n ist positive Ganzzahl)
feste Anzahl:
Beziehung zu genau n Instanzen
Auf jeder Rechnung wird genau eine Lieferanschrift genannt; Jede Rechnungsposition ist genau einer Rechnung zugeordnet. Da hier Unter- und Obergrenze identisch sind (z.B. 3..3) kann eine Grenze weggelassen werden (3).
0..1 optional:
Beziehung zu keiner oder einer Instanz
Eine Rechnungsanschrift ist optional. Eine Rechnung kann eine gesonderte Rechnungsanschrift haben (andernfalls ist sie identisch mit der Lieferanschrift).
m..n

(n und m sind Ganzzahlen, 0 <= m < n)
Beziehungen zwischen m und n unterschiedlichen Instanzen Es gibt genau zwei Steuersätze (ermäßigt/normal). Auf jeder Rechung wird mindestens ein Steuersatz ausgewiesen, höchstens aber beide.
* oder
0..*
null oder mehr
Beziehung zu beliebig vielen Instanzen (oder zu keiner)
Ein Steuersatz kann auf beliebig vielen Rechnungen ausgewiesen sein. Denkbar auch, dass er auf keiner Rechnung ausgewiesen wird. Anstelle von 0..* kann auch verkürzt * notiert werden.
n..*

(n ist positive Ganzzahl)
mindestens n oder mehr Auf jeder Rechnung findet sich mindestens eine Rechnungsposition.

Im Programmtext muss anhand des Datentyps der Referenz oder über Implementierung sichergestellt werden, dass die jeweilige Anzahl an Instanzen übergeben werden kann.

Die Begriffe Kardinalität und Multiplizität

In der UML wird eine tatsächliche Anzahl der verknüpften Instanzen Kardinaltät genannt. An folgendem Beispiel soll das deutlich werden: Ein Fahrzeug kann eines oder viele Räder haben.

Rechnung, Rechnungsanschrift, Lieferanschrift, Rechnungsposition und Steuersatz
Rechnung, Rechnungsanschrift, Lieferanschrift, Rechnungsposition und Steuersatz

Ist das Fahrzeug ein Fahrrad, so beträgt die tatsächliche Anzahl (die Kardinalität) 2, bei einem Auto 4, bei einem Einrad 1, bei einer Ape 3 und bei einem Zug ein Vielfaches von 4 - z.B. 128. Der Begriff Kardinalität beschreibt in der UML also die konkrete Anzahl an Beziehungen einer identifizierten Instanz der Klasse. Das weicht von der Bedeutung ab, mit der Kardinalität bei der Datenbankmodellierung (Entity Relationship Model) verwendet wird - dort ist Kardinalität synonym zu Multiplizität zu sehen.

In der UML ist der Begriff Multiplizität definiert als die Menge aller zulässigen Kardinalitäten. Ein Fahrzeug allgemein kann ein oder viele Räder haben: Die Multiplizität ist daher 1..*.

Aggregation

Die bisherigen Assoziationen beschreiben eine temporäre, einem Ziel zugeordnete Zusammenarbeit zweier Objekte. Wir können diese mit “benutzt ein” beschreiben. Eine engere Bindung zweiter Objekte ist gegeben, wenn Objekte zueinander eine dauerhafte Teil-Ganzes-Beziehung haben, die mit Phrasen wie “besteht aus” oder “hat ein/e” beschrieben werden können (in umgekehrter Leserichtung: “ist Teil von”).

Die zugeordneten Instanzen der Teile werden i.d.R. in Attributen des Ganzen verwaltet. Per Setter-Methoden wird das Ganze (das Aggregat) zusammengesetzt, somit im wörtlichen Sinne aggregiert. Man nennt diese Beziehung daher Aggregation.

Im UML-Klassendiagramm wird diese besondere Assoziation durch eine nicht ausgefüllte Raute aufseiten des Ganzen notiert.

Ein Konto ist Teil des Objekts Kunde
Ein Konto ist Teil des Objekts Kunde

Bei einer Aggregation kann das Teil auch unabhängig vom Ganzen existieren - es gibt Referenzen auf das Teil außerhalb des Ganzen (z.B. in dem die Teil-Instanz außerhalb des Ganzen erzeugt wird).

Es kann auch Teilinstanzen geben, die mehreren Ganzen zugeordnet sind. Denkbar wäre im obigen Beispiel etwa, dass Konten mehreren Kundinnen zugeordnet werden oder eine Kundin gelöscht wird, die Konten aber weiterbestehen. Ein Quelltextbeispiel:

Komposition

Bei der Komposition handelt es sich um eine Sonderform der Teil/Ganzes-Beziehung: Die Teilobjekte existieren nur innerhalb des Ganzen und sind somit in Ihrer Lebensdauer auf die Lebensdauer des zugehörigen Ganzen beschränkt. Da dies eine festere Bindung zwischen Teil und Ganzem ist, wird in der Literatur hierfür häufig auch der Begriff “starke Aggregation” gewählt.

Aufgrund dieser Abhängigkeit kann ein Teil auch nur mit einem Ganzen in Beziehung stehen - die Multiplizität aufseiten des Ganzen ist per Definition in der Regel “1” (der Defaultwert) wird daher oft weggelassen.

Die Komposition wird mir einer ausgefüllten Raute aufseiten des Ganzen gekennzeichnet:

Eine Rechnungsposition ist existenziell abhängig von einer Rechnung
Eine Rechnungsposition ist existenziell abhängig von einer Rechnung

Kompositionen erkennt man in der Programmierung in der Regel daran, dass das Teil innerhalb des Ganzen erzeugt wird und das Teilobjekt nicht über Getter oder öffentliche Attribute nach außen veröffentlicht wird. Nur das Ganze hält Referenzen auf das Teil.

Die Entscheidung, ob eine Beziehung als Aggregation oder Komposition umgesetzt wird, trifft der/die Programmierer*in. Häufig gibt die Fachdomäne über gegebene Abhängigkeiten bereits Hinweise, was sinnvoll ist. In der Regel führt die Komposition zu besser wartbarem Code, da weniger externe Abhängigkeiten möglich sind.

Quelltextbeispiel:

Für Experten / Hintergrund

Die UML lässt die Möglichkeit, dass Teile vom Ganzen entkoppelt werden und dann auch nicht mit dem Ganzen gelöscht werden. Um diesen Sonderfall abzubilden, müsste man eine Getter-Methode implementieren, die innerhalb des Ganzen die Referenz auf das Teil löscht, sobald es das Ganze verlässt. In obigem Beispiel könnte so eine Methode in Rechnung etwa so aussehen:

Übersicht von Assoziation, Aggregation und Komposition

Die unterschiedlichen Objektbeziehungen anhand der oben genannten Beispiele ergeben insgesamt die Modellierung eines Abrechnungssystems:

Die unterschiedlichen Objektbeziehungen zwischen Konto, Kunde, Rechnung und Rechnungsposition als UML-Klassendiagramm
Die unterschiedlichen Objektbeziehungen zwischen Konto, Kunde, Rechnung und Rechnungsposition als UML-Klassendiagramm

Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: Objektbeziehungen - Assoziationen” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/uml-klassendiagramm-assoziation veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/uml/umlklasse. Stand: 24.05.2023.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: