OOP Klassenbeziehungen: Assoziation, Aggregation, Komposition & Vererbung
Dieser Beitrag ist eine umfassende Erläuterung der Klassenbeziehungen in der OOP – inklusive Assoziation, Aggregation, Komposition und Vererbung mit praktischen Beispielen.
In a Nutshell
Klassenbeziehungen beschreiben, wie Objekte miteinander interagieren und zusammenarbeiten, um komplexe Systeme zu bilden. Die vier Hauptbeziehungsarten sind Assoziation, Aggregation, Komposition und Vererbung.
Kompakte Fachbeschreibung
Klassenbeziehungen definieren die Art und Weise, wie Klassen und ihre Objekte miteinander verbunden sind. Sie sind fundamental für die Strukturierung objektorientierter Systeme.
Vier Hauptbeziehungsarten:
1. Assoziation (hat-ein)
- Beschreibung: Zwei Klassen sind miteinander verbunden
- Lebenszyklus: Unabhängig voneinander
- Beispiel:
KundehatBestellungen - UML: Einfache Linie zwischen Klassen
2. Aggregation (besteht-aus)
- Beschreibung: “hat-ein” Beziehung, Teile können unabhängig existieren
- Lebenszyklus: Teil kann ohne Ganzes existieren
- Beispiel:
AutohatReifen - UML: Linie mit leerer Raute am Ganzen
3. Komposition (ist-Teil-von)
- Beschreibung: “ist-Teil-von” Beziehung, Teile existieren nur mit Ganzen
- Lebenszyklus: Teil kann nicht ohne Ganzes existieren
- Beispiel:
AutohatMotor - UML: Linie mit gefüllter Raute am Ganzen
4. Vererbung (ist-ein)
- Beschreibung: “ist-ein” Beziehung, Spezialisierung
- Lebenszyklus: Unterklasse erbt von Oberklasse
- Beispiel:
PKWist einFahrzeug - UML: Linie mit leerem Pfeil zur Oberklasse
Prüfungsrelevante Stichpunkte
- Assoziation: Einfache Beziehung zwischen Klassen
- Aggregation: “hat-ein” Beziehung mit unabhängigen Teilen
- Komposition: “ist-Teil-von” Beziehung mit abhängigen Teilen
- Vererbung: “ist-ein” Beziehung mit Spezialisierung
- Multiplizitäten: 1, , 0..1, 1.., 0..*
- UML-Notation: Verschiedene Pfeile und Raute-Symbole
- Lebenszyklus: Abhängigkeit der Objekte zueinander
- IHK-relevant: Wichtig für Softwarearchitektur und -design
Kernkomponenten
- Assoziation: Bidirektionale oder unidirektionale Beziehung
- Aggregation: Schwache “hat-ein” Beziehung
- Komposition: Starke “ist-Teil-von” Beziehung
- Vererbung: Spezialisierung und Wiederverwendung
- Multiplizität: Anzahl der Beziehungsinstanzen
- Rollen: Bezeichnung der Beziehungsrolle
- Navigierbarkeit: Richtung der Beziehung
- Qualifizierer: Zusatzinformation zur Beziehung
Praxisbeispiele
1. Assoziation (Kunde - Bestellung)
// Bidirektionale Assoziation
public class Kunde {
private String kundenId;
private String name;
private List<Bestellung> bestellungen = new ArrayList<>();
public void addBestellung(Bestellung bestellung) {
bestellungen.add(bestellung);
bestellung.setKunde(this); // Rückreferenz
}
public List<Bestellung> getBestellungen() {
return new ArrayList<>(bestellungen);
}
}
public class Bestellung {
private String bestellId;
private Date bestelldatum;
private Kunde kunde; // Rückreferenz zum Kunden
public void setKunde(Kunde kunde) {
this.kunde = kunde;
}
public Kunde getKunde() {
return kunde;
}
}
// Verwendung
Kunde meier = new Kunde("1", "Meier");
Bestellung b1 = new Bestellung("B001", new Date());
Bestellung b2 = new Bestellung("B002", new Date());
meier.addBestellung(b1);
meier.addBestellung(b2);
2. Aggregation (Auto - Reifen)
// Aggregation: Auto hat Reifen, Reifen können ohne Auto existieren
public class Auto {
private String modell;
private List<Reifen> reifen = new ArrayList<>();
public Auto(String modell) {
this.modell = modell;
}
public void addReifen(Reifen reifen) {
if (reifen.size() < 4) {
this.reifen.add(reifen);
}
}
public void removeReifen(Reifen reifen) {
this.reifen.remove(reifen);
// Reifen existiert weiter und kann anderem Auto zugewiesen werden
}
}
public class Reifen {
private String hersteller;
private int groesse;
public Reifen(String hersteller, int groesse) {
this.hersteller = hersteller;
this.groesse = groesse;
}
// Reifen kann unabhängig vom Auto existieren
public void montieren() {
System.out.println("Reifen wird montiert");
}
}
// Verwendung
Auto golf = new Auto("Golf");
Reifen michelin1 = new Reifen("Michelin", 195);
Reifen michelin2 = new Reifen("Michelin", 195);
golf.addReifen(michelin1);
golf.addReifen(michelin2);
// Reifen können entfernt und wiederverwendet werden
golf.removeReifen(michelin1);
3. Komposition (Auto - Motor)
// Komposition: Motor existiert nur mit Auto
public class Auto {
private String modell;
private Motor motor; // Motor kann nicht ohne Auto existieren
public Auto(String modell, int leistung) {
this.modell = modell;
this.motor = new Motor(leistung); // Motor wird intern erstellt
}
public void starten() {
motor.starten();
System.out.println(modell + " wird gestartet");
}
public void ausschalten() {
motor.ausschalten();
System.out.println(modell + " wird ausgeschaltet");
}
// Motor wird mit Auto zerstört
protected void finalize() {
// Motor wird automatisch aufgeräumt
}
}
public class Motor {
private int leistung;
private boolean laeuft;
// Privater Konstruktor - nur Auto kann Motor erstellen
protected Motor(int leistung) {
this.leistung = leistung;
this.laeuft = false;
}
protected void starten() {
this.laeuft = true;
System.out.println("Motor mit " + leistung + " PS wird gestartet");
}
protected void ausschalten() {
this.laeuft = false;
System.out.println("Motor wird ausgeschaltet");
}
}
// Verwendung
Auto bmw = new Auto("BMW", 200);
bmw.starten(); // Motor wird intern gestartet
bmw.ausschalten();
// Motor kann nicht unabhängig erstellt werden:
// Motor motor = new Motor(150); // Fehler: Konstruktor ist protected
4. Vererbung (Fahrzeug - Auto)
// Oberklasse
public abstract class Fahrzeug {
protected String marke;
protected int baujahr;
protected int aktuelleGeschwindigkeit = 0;
public Fahrzeug(String marke, int baujahr) {
this.marke = marke;
this.baujahr = baujahr;
}
// Gemeinsame Methoden
public void beschleunigen(int kmh) {
this.aktuelleGeschwindigkeit += kmh;
System.out.println(marke + " beschleunigt auf " + aktuelleGeschwindigkeit + " km/h");
}
public void bremsen(int kmh) {
if (aktuelleGeschwindigkeit >= kmh) {
this.aktuelleGeschwindigkeit -= kmh;
System.out.println(marke + " bremst auf " + aktuelleGeschwindigkeit + " km/h");
}
}
// Abstrakte Methode - muss von Unterklassen implementiert werden
public abstract void hupen();
// Konkrete Methode - kann überschrieben werden
public void anzeigen() {
System.out.println("Fahrzeug: " + marke + ", Baujahr: " + baujahr +
", Geschwindigkeit: " + aktuelleGeschwindigkeit + " km/h");
}
}
// Unterkasse
public class Auto extends Fahrzeug {
private int anzahlTueren;
private boolean klimaanlage;
public Auto(String marke, int baujahr, int anzahlTueren) {
super(marke, baujahr); // Oberklassen-Konstruktor aufrufen
this.anzahlTueren = anzahlTueren;
this.klimaanlage = false;
}
// Implementierung der abstrakten Methode
@Override
public void hupen() {
System.out.println("Auto hupt: Tut Tut!");
}
// Zusätzliche Methode nur für Auto
public void klimaanlageEin() {
klimaanlage = true;
System.out.println("Klimaanlage eingeschaltet");
}
// Überschreiben der Oberklassen-Methode
@Override
public void anzeigen() {
super.anzeigen(); // Oberklassen-Methode aufrufen
System.out.println(" Typ: Auto, Türen: " + anzahlTueren +
", Klimaanlage: " + (klimaanlage ? "an" : "aus"));
}
}
// Weitere Unterklasse
public class Motorrad extends Fahrzeug {
private boolean hatSeitenwagen;
public Motorrad(String marke, int baujahr, boolean hatSeitenwagen) {
super(marke, baujahr);
this.hatSeitenwagen = hatSeitenwagen;
}
@Override
public void hupen() {
System.out.println("Motorrad hupt: Iiih Iiih!");
}
public void wheelie() {
System.out.println("Motorrad macht Wheelie!");
}
@Override
public void anzeigen() {
super.anzeigen();
System.out.println(" Typ: Motorrad, Seitenwagen: " +
(hatSeitenwagen ? "ja" : "nein"));
}
}
// Verwendung
Fahrzeug golf = new Auto("Volkswagen", 2023, 5);
Fahrzeug harley = new Motorrad("Harley-Davidson", 2022, false);
golf.hupen(); // Auto hupt: Tut Tut!
harley.hupen(); // Motorrad hupt: Iiih Iiih!
golf.beschleunigen(50);
harley.beschleunigen(80);
golf.anzeigen();
harley.anzeigen();
// Downcasting für spezifische Methoden
if (golf instanceof Auto) {
Auto autoGolf = (Auto) golf;
autoGolf.klimaanlageEin();
}
if (harley instanceof Motorrad) {
Motorrad motorradHarley = (Motorrad) harley;
motorradHarley.wheelie();
}
UML-Notation für Beziehungen
Assoziation
Kunde 1..* --* Bestellung
Aggregation
Auto 1 --* Reifen
Komposition
Auto 1 --* Motor
Vererbung
Fahrzeug <|-- Auto
Fahrzeug <|-- Motorrad
Multiplizitäten
| Symbol | Bedeutung | Beispiel |
|---|---|---|
| 1 | Genau eins | 1 Motor |
| 0..1 | Null oder eins | 0..1 Führerschein |
| * | Null oder mehr | * Reifen |
| 1..* | Mindestens eins | 1..* Türen |
| 2..4 | Zwischen 2 und 4 | 2..4 Räder |
Entscheidungshilfe für Beziehungsarten
Wann welche Beziehung?
Assoziation verwenden wenn:
- Zwei Klassen interagieren, aber keine Abhängigkeit besteht
- Beziehung ist temporär oder optional
- Objekte existieren unabhängig voneinander
Aggregation verwenden wenn:
- “hat-ein” Beziehung vorliegt
- Teile können ohne Ganzes existieren
- Teile können zwischen verschiedenen Ganzen geteilt werden
Komposition verwenden wenn:
- “ist-Teil-von” Beziehung vorliegt
- Teile existieren nur mit dem Ganzen
- Lebenszyklus des Teils ist an das Ganze gebunden
Vererbung verwenden wenn:
- “ist-ein” Beziehung vorliegt
- Unterklasse ist eine spezielle Form der Oberklasse
- Code-Wiederverwendung und Polymorphie gewünscht
Vorteile und Nachteile
Vorteile von Klassenbeziehungen
- Strukturierung: Klare Systemarchitektur
- Wiederverwendung: Gemeinsame Funktionalität
- Flexibilität: Leichte Erweiterbarkeit
- Verständlichkeit: Reale Welt wird abgebildet
- Wartbarkeit: Gezielte Änderungen möglich
Nachteile
- Komplexität: Viele Beziehungen können unübersichtlich werden
- Kopplung: Starke Abhängigkeiten können Probleme verursachen
- Performance: Zu viele Objektverbindungen können langsam sein
- Testbarkeit: Komplexe Beziehungen schwer zu testen
Häufige Prüfungsfragen
-
Was ist der Unterschied zwischen Aggregation und Komposition? Aggregation: Teile können unabhängig existieren, Komposition: Teile existieren nur mit dem Ganzen.
-
Erklären Sie die Multiplizität 1..*! Mindestens eins, beliebig viele Objekte können verbunden sein.
-
Wann verwendet man Vererbung statt Komposition? Wenn eine “ist-ein” Beziehung besteht und Code-Wiederverwendung gewünscht ist.
-
Was bedeutet bidirektionale Assoziation? Beide Klassen kennen sich gegenseitig und können aufeinander zugreifen.
Wichtigste Quellen
- https://de.wikipedia.org/wiki/Assoziation_(UML)
- https://refactoring.guru/design-patterns/composition-over-inheritance
- https://www.uml-diagrams.org/class-diagram-relationships.html