UML Class Diagrams: Relationships, Association, Aggregation & Composition
UML class diagrams are the most important tool for visualizing object-oriented software architectures. Relationships between classes define the structure and behavior of the system.
Basics of UML Class Diagrams
Class Representation
Simple Class with Interface
@startuml
' Basis-Klasse mit Attributen und Methoden
class Student {
-matrikelNr: int
-name: String
-semester: int
+getName(): String
+setMatrikelNr(nr: int): void
+studieren(): void
}
' Interface mit Methoden
interface Lernbar {
+lernen(): void
+pruefungAblegen(): boolean
}
' Interface
interface Lernfaehig {
+{abstract} lernen(fach: String): void
+{abstract} pruefungAblegen(fach: String): boolean
}
' Student implementiert Lernbar
Student ..|> Lernbar
@enduml
Visibility Modifiers
| Symbol | Meaning | Description |
|---|---|---|
+ | public | Accessible from anywhere |
- | private | Only within the class |
# | protected | Within the class and subclasses |
~ | package | Only within the package |
Relationship Types in UML
1. Association
Associations describe structural relationships between classes.
@startuml
class Student {
-name: String
+getName(): String
}
class Kurs {
-titel: String
-credits: int
+getTitel(): String
}
' Einfache Assoziation
Student "1" -- "n" Kurs : belegt >
' Assoziation mit Rollen und Attributen
Student "1" -- "*" Note : \
note : hat
note : noteWert: double
note : datum: Date
' Gerichtete Assoziation
Student "1" -> "*" Projekt : leitet >
@enduml
Java Implementation of Associations
public class AssociationExamples {
// Many-to-many association
public class Student {
private String name;
private List<Kurs> belegteKurse = new ArrayList<>();
private List<Note> noten = new ArrayList<>();
private List<Projekt> geleiteteProjekte = new ArrayList<>();
public void belegeKurs(Kurs kurs) {
belegteKurse.add(kurs);
kurs.addStudent(this);
}
public void addNote(Note note) {
noten.add(note);
}
public void leiteProjekt(Projekt projekt) {
geleiteteProjekte.add(projekt);
}
// Getters and setters
public String getName() { return name; }
public List<Kurs> getBelegteKurse() { return new ArrayList<>(belegteKurse); }
}
public class Kurs {
private String titel;
private int credits;
private List<Student> studenten = new ArrayList<>();
public void addStudent(Student student) {
studenten.add(student);
}
// Getters and setters
public String getTitel() { return titel; }
public List<Student> getStudenten() { return new ArrayList<>(studenten); }
}
public class Note {
private double noteWert;
private Date datum;
private Student student;
public Note(double noteWert, Date datum, Student student) {
this.noteWert = noteWert;
this.datum = datum;
this.student = student;
student.addNote(this);
}
// Getters and setters
public double getNoteWert() { return noteWert; }
public Date getDatum() { return datum; }
}
public class Projekt {
private String name;
private Student leiter;
private List<Student> mitglieder = new ArrayList<>();
public Projekt(String name, Student leiter) {
this.name = name;
this.leiter = leiter;
leiter.leiteProjekt(this);
}
// Getters and setters
public String getName() { return name; }
public Student getLeiter() { return leiter; }
}
}
2. Aggregation
Aggregation is a special form of association where a part object can exist without the whole.
@startuml
class Abteilung {
-name: String
+getName(): String
}
class Mitarbeiter {
-name: String
-position: String
+getName(): String
}
' Aggregation (empty diamond)
Abteilung o-- "*" Mitarbeiter : hat >
class Universitaet {
-name: String
+getName(): String
}
class Fakultaet {
-name: String
+getName(): String
}
' Aggregation
Universitaet o-- "*" Fakultaet : besitzt >
@enduml
Java Implementation of Aggregation
public class AggregationExamples {
// Department can exist without employees
public class Abteilung {
private String name;
private List<Mitarbeiter> mitarbeiter = new ArrayList<>();
public Abteilung(String name) {
this.name = name;
}
public void addMitarbeiter(Mitarbeiter mitarbeiter) {
this.mitarbeiter.add(mitarbeiter);
}
public void removeMitarbeiter(Mitarbeiter mitarbeiter) {
this.mitarbeiter.remove(mitarbeiter);
}
// Employees can also exist without a department
public String getName() { return name; }
public List<Mitarbeiter> getMitarbeiter() {
return new ArrayList<>(mitarbeiter);
}
}
public class Mitarbeiter {
private String name;
private String position;
private Abteilung abteilung; // Optional
public Mitarbeiter(String name, String position) {
this.name = name;
this.position = position;
}
public void setAbteilung(Abteilung abteilung) {
this.abteilung = abteilung;
if (abteilung != null) {
abteilung.addMitarbeiter(this);
}
}
public void removeAbteilung() {
if (abteilung != null) {
abteilung.removeMitarbeiter(this);
this.abteilung = null;
}
}
// Employees exist independently of department
public String getName() { return name; }
public Abteilung getAbteilung() { return abteilung; }
}
}
3. Composition
Composition is a stronger form of aggregation where the part object cannot exist without the whole.
@startuml
class Auto {
-marke: String
-modell: String
+fahren(): void
}
class Motor {
-leistung: int
-typ: String
+starten(): void
}
class Reifen {
-groesse: int
-druck: double
+aufpumpen(): void
}
' Composition (filled diamond)
Auto *-- "1" Motor : hat >
Auto *-- "4" Reifen : hat >
class Haus {
-adresse: String
+wohnen(): void
}
class Zimmer {
-flaeche: double
-typ: String
+reinigen(): void
}
' Composition
Haus *-- "*" Zimmer : hat >
@enduml
Java Implementation of Composition
public class CompositionExamples {
// Engine exists only in the car
public class Auto {
private String marke;
private String modell;
private Motor motor; // Exists only with car
private List<Reifen> reifen = new ArrayList<>(); // Exist only with car
public Auto(String marke, String modell) {
this.marke = marke;
this.modell = modell;
this.motor = new Motor(200, "Diesel"); // Engine is created in car
this.reifen.addAll(Arrays.asList(
new Reifen(205), new Reifen(205),
new Reifen(205), new Reifen(205)
));
}
public void fahren() {
motor.starten();
System.out.println(marke + " " + modell + " fährt");
}
// Getters
public String getMarke() { return marke; }
public Motor getMotor() { return motor; }
public List<Reifen> getReifen() {
return new ArrayList<>(reifen);
}
}
public class Motor {
private int leistung;
private String typ;
// Private constructor - only callable by car
private Motor(int leistung, String typ) {
this.leistung = leistung;
this.typ = typ;
}
public void starten() {
System.out.println("Motor (" + typ + ", " + leistung + " PS) gestartet");
}
// Getters
public int getLeistung() { return leistung; }
public String getTyp() { return typ; }
}
public class Reifen {
private int groesse;
private double druck;
// Private constructor
private Reifen(int groesse) {
this.groesse = groesse;
this.druck = 2.5; // Standard pressure
}
public void aufpumpen(double druck) {
this.druck = druck;
System.out.println("Reifen auf " + druck + " bar aufgepumpt");
}
// Getters
public int getGroesse() { return groesse; }
public double getDruck() { return druck; }
}
}
4. Dependency
Dependencies show usage relationships between classes.
@startuml
class Bestellung {
-datum: Date
-gesamtsumme: double
+berechneGesamtsumme(): double
}
class PreisRechner {
+berechnePreis(produkte: List<Produkt>): double
+berechneRabatt(betrag: double, rabatt: double): double
}
' Dependency (dashed line)
Bestellung ..> PreisRechner : uses >
class Logger {
+logInfo(nachricht: String): void
+logError(nachricht: String): void
}
class DatenbankService {
+speichern(daten: Object): void
+laden(id: String): Object
}
Bestellung ..> Logger : logs >
Bestellung ..> DatenbankService : saves >
@enduml
Java Implementation of Dependencies
public class DependencyExamples {
public class Bestellung {
private Date datum;
private List<Produkt> produkte = new ArrayList<>();
private PreisRechner preisRechner;
private Logger logger;
private DatenbankService datenbankService;
public Bestellung(PreisRechner preisRechner, Logger logger,
DatenbankService datenbankService) {
this.datum = new Date();
this.preisRechner = preisRechner;
this.logger = logger;
this.datenbankService = datenbankService;
}
public void addProdukt(Produkt produkt) {
produkte.add(produkt);
logger.logInfo("Produkt hinzugefügt: " + produkt.getName());
}
public double berechneGesamtsumme() {
double summe = preisRechner.berechnePreis(produkte);
double rabatt = preisRechner.berechneRabatt(summe, 0.1);
logger.logInfo("Gesamtsumme berechnet: " + (summe - rabatt));
return summe - rabatt;
}
public void speichern() {
try {
datenbankService.speichern(this);
logger.logInfo("Bestellung gespeichert");
} catch (Exception e) {
logger.logError("Fehler beim Speichern: " + e.getMessage());
}
}
// Getter
public Date getDatum() { return datum; }
public List<Produkt> getProdukte() { return new ArrayList<>(produkte); }
}
// Dependency classes
public class PreisRechner {
public double berechnePreis(List<Produkt> produkte) {
return produkte.stream()
.mapToDouble(Produkt::getPreis)
.sum();
}
public double berechneRabatt(double betrag, double rabattProzent) {
return betrag * rabattProzent;
}
}
public class Logger {
public void logInfo(String nachricht) {
System.out.println("INFO: " + nachricht);
}
public void logError(String nachricht) {
System.err.println("ERROR: " + nachricht);
}
}
public class DatenbankService {
public void speichern(Object daten) {
System.out.println("Daten gespeichert: " + daten);
}
public Object laden(String id) {
System.out.println("Daten geladen: " + id);
return null;
}
}
public class Produkt {
private String name;
private double preis;
public Produkt(String name, double preis) {
this.name = name;
this.preis = preis;
}
// Getter
public String getName() { return name; }
public double getPreis() { return preis; }
}
}
5. Inheritance
Inheritance shows “is-a” relationships between classes.
@startuml
' Abstract base class
abstract class Fahrzeug {
#marke: String
#baujahr: int
+{abstract} fahren(): void
+{abstract} bremsen(): void
+getInfo(): String
}
' Concrete subclasses
class Auto extends Fahrzeug {
-anzahlTueren: int
+fahren(): void
+bremsen(): void
+offnen(): void
}
class Motorrad extends Fahrzeug {
-typ: String
+fahren(): void
+bremsen(): void
+lenken(): void
}
class Fahrrad extends Fahrzeug {
-anzahlGaenge: int
+fahren(): void
+bremsen(): void
+schalten(): void
}
@enduml
Java Implementation of Inheritance
public class InheritanceExamples {
// Abstract base class
public abstract class Fahrzeug {
protected String marke;
protected int baujahr;
public Fahrzeug(String marke, int baujahr) {
this.marke = marke;
this.baujahr = baujahr;
}
// Abstract methods
public abstract void fahren();
public abstract void bremsen();
// Concrete method
public String getInfo() {
return marke + " aus " + baujahr;
}
// Getter
public String getMarke() { return marke; }
public int getBaujahr() { return baujahr; }
}
// Concrete subclasses
public class Auto extends Fahrzeug {
private int anzahlTueren;
public Auto(String marke, int baujahr, int anzahlTueren) {
super(marke, baujahr);
this.anzahlTueren = anzahlTueren;
}
@Override
public void fahren() {
System.out.println("Auto " + marke + " fährt mit " + anzahlTueren + " Türen");
}
@Override
public void bremsen() {
System.out.println("Auto bremst mit Bremsscheiben");
}
public void offnen() {
System.out.println("Autotür wird geöffnet");
}
@Override
public String getInfo() {
return super.getInfo() + " (Auto, " + anzahlTueren + " Türen)";
}
}
public class Motorrad extends Fahrzeug {
private String typ;
public Motorrad(String marke, int baujahr, String typ) {
super(marke, baujahr);
this.typ = typ;
}
@Override
public void fahren() {
System.out.println("Motorrad " + marke + " (" + typ + ") fährt");
}
@Override
public void bremsen() {
System.out.println("Motorrad bremst mit Scheibenbremsen");
}
public void lenken() {
System.out.println("Motorrad wird gelenkt");
}
@Override
public String getInfo() {
return super.getInfo() + " (Motorrad, " + typ + ")";
}
}
public class Fahrrad extends Fahrzeug {
private int anzahlGaenge;
public Fahrrad(String marke, int baujahr, int anzahlGaenge) {
super(marke, baujahr);
this.anzahlGaenge = anzahlGaenge;
}
@Override
public void fahren() {
System.out.println("Fahrrad " + marke + " wird in die Pedale getreten");
}
@Override
public void bremsen() {
System.out.println("Fahrrad bremst mit Felgenbremsen");
}
public void schalten() {
System.out.println("Fahrrad schaltet Gang");
}
@Override
public String getInfo() {
return super.getInfo() + " (Fahrrad, " + anzahlGaenge + " Gänge)";
}
}
}
6. Implementation
Implementation shows the relationship between interfaces and implementing classes.
@startuml
' Interfaces
interface Fliegend {
+{abstract} fliegen(): void
+{abstract} landen(): void
}
interface Schwimmend {
+{abstract} schwimmen(): void
+{abstract} tauchen(): void
}
' Klasse implementiert ein Interface
class Vogel implements Fliegend {
-art: String
+fliegen(): void
+landen(): void
}
' Klasse implementiert mehrere Interfaces
class Ente implements Fliegend, Schwimmend {
-rasse: String
+fliegen(): void
+landen(): void
+schwimmen(): void
+tauchen(): void
}
' Abstrakte Klasse implementiert Interface
abstract class Wasserlebewesen implements Schwimmend {
-lebensraum: String
+{abstract} atmen(): void
+schwimmen(): void
+tauchen(): void
}
class Fisch extends Wasserlebewesen {
-art: String
+atmen(): void
}
@enduml
Java Implementation of Interfaces
public class InterfaceExamples {
// Interfaces
public interface Fliegend {
void fliegen();
void landen();
}
public interface Schwimmend {
void schwimmen();
void tauchen();
}
// Simple implementation
public class Vogel implements Fliegend {
private String art;
public Vogel(String art) {
this.art = art;
}
@Override
public void fliegen() {
System.out.println(art + " fliegt in die Luft");
}
@Override
public void landen() {
System.out.println(art + " landet sanft");
}
public String getArt() { return art; }
}
// Implementing multiple interfaces
public class Ente implements Fliegend, Schwimmend {
private String rasse;
public Ente(String rasse) {
this.rasse = rasse;
}
@Override
public void fliegen() {
System.out.println(rasse + " fliegt in Formation");
}
@Override
public void landen() {
System.out.println(rasse + " landet auf dem Wasser");
}
@Override
public void schwimmen() {
System.out.println(rasse + " schwimmt elegant");
}
@Override
public void tauchen() {
System.out.println(rasse + " taucht nach Fischen");
}
public String getRasse() { return rasse; }
}
// Abstract class with interface
public abstract class Wasserlebewesen implements Schwimmend {
protected String lebensraum;
public Wasserlebewesen(String lebensraum) {
this.lebensraum = lebensraum;
}
public abstract void atmen();
@Override
public void schwimmen() {
System.out.println("Schwimmt im " + lebensraum);
}
@Override
public void tauchen() {
System.out.println("Taucht tief im " + lebensraum);
}
public String getLebensraum() { return lebensraum; }
}
// Concrete subclass
public class Fisch extends Wasserlebewesen {
private String art;
public Fisch(String art, String lebensraum) {
super(lebensraum);
this.art = art;
}
@Override
public void atmen() {
System.out.println(art + " atmet mit Kiemen im Wasser");
}
public String getArt() { return art; }
}
}
Complex Class Diagrams
Complete Example: University System
@startuml
' Abstract base classes
abstract class Person {
#name: String
#alter: int
+{abstract} getInfo(): String
+geburtstagFeiern(): void
}
abstract class Mitarbeiter extends Person {
#mitarbeiterNr: String
#gehalt: double
+{abstract} arbeiten(): void
+gehaltErhoehen(prozent: double): void
}
' Concrete classes
class Student extends Person {
-matrikelNr: String
-fach: String
+studieren(): void
+pruefungAblegen(): boolean
+getInfo(): String
}
class Dozent extends Mitarbeiter {
-fachbereich: String
+arbeiten(): void
+vorlesungHalten(): void
+klausurKorrigieren(): void
+getInfo(): String
}
class Verwaltungsmitarbeiter extends Mitarbeiter {
-abteilung: String
+arbeiten(): void
+dokumenteVerwalten(): void
+getInfo(): String
}
' Additional classes
class Kurs {
-titel: String
-credits: int
-dozent: Dozent
+getTitel(): String
+setDozent(dozent: Dozent): void
}
class Pruefung {
-datum: Date
-note: double
-student: Student
-kurs: Kurs
+noteEintragen(note: double): void
+bestanden(): boolean
}
class Raum {
-nummer: String
-kapazitaet: int
+gebucht(): boolean
+reservieren(): void
}
' Relationships
Person <|-- Student
Person <|-- Mitarbeiter
Mitarbeiter <|-- Dozent
Mitarbeiter <|-- Verwaltungsmitarbeiter
Dozent "1" -- "*" Kurs : unterrichtet >
Student "n" -- "n" Kurs : belegt >
Student "n" -- "n" Pruefung : schreibt >
Kurs "1" -- "n" Pruefung : hat >
Kurs "n" -- "1" Raum : findetStattIn >
' Aggregation
Fakultaet o-- "*" Dozent : beschaeftigt >
Fakultaet o-- "*" Student : immatrikuliert >
' Composition
Universitaet *-- "*" Fakultaet : besitzt >
@enduml
Java Implementation of the University System
public class UniversitySystem {
// Abstract base class
public abstract class Person {
protected String name;
protected int alter;
public Person(String name, int alter) {
this.name = name;
this.alter = alter;
}
public abstract String getInfo();
public void geburtstagFeiern() {
alter++;
System.out.println("Herzlichen Glückwunsch zum " + alter + ". Geburtstag, " + name + "!");
}
// Getter
public String getName() { return name; }
public int getAlter() { return alter; }
}
// Abstract employee class
public abstract class Mitarbeiter extends Person {
protected String mitarbeiterNr;
protected double gehalt;
public Mitarbeiter(String name, int alter, String mitarbeiterNr, double gehalt) {
super(name, alter);
this.mitarbeiterNr = mitarbeiterNr;
this.gehalt = gehalt;
}
public abstract void arbeiten();
public void gehaltErhoehen(double prozent) {
gehalt = gehalt * (1 + prozent / 100);
System.out.println("Gehalt erhöht auf: " + gehalt);
}
// Getter
public String getMitarbeiterNr() { return mitarbeiterNr; }
public double getGehalt() { return gehalt; }
}
// Concrete classes
public class Student extends Person {
private String matrikelNr;
private String fach;
private List<Kurs> belegteKurse = new ArrayList<>();
private List<Pruefung> pruefungen = new ArrayList<>();
public Student(String name, int alter, String matrikelNr, String fach) {
super(name, alter);
this.matrikelNr = matrikelNr;
this.fach = fach;
}
@Override
public String getInfo() {
return "Student: " + name + " (" + matrikelNr + "), Fach: " + fach;
}
public void studieren() {
System.out.println(name + " studiert " + fach);
}
public boolean pruefungAblegen() {
return belegteKurse.stream().anyMatch(kurs ->
pruefungen.stream()
.anyMatch(pruefung ->
pruefung.getKurs().equals(kurs) && pruefung.bestanden()
)
);
}
public void belegeKurs(Kurs kurs) {
belegteKurse.add(kurs);
kurs.addStudent(this);
}
public void addPruefung(Pruefung pruefung) {
pruefungen.add(pruefung);
}
// Getter
public String getMatrikelNr() { return matrikelNr; }
public String getFach() { return fach; }
public List<Kurs> getBelegteKurse() { return new ArrayList<>(belegteKurse); }
}
public class Dozent extends Mitarbeiter {
private String fachbereich;
private List<Kurs> unterrichteteKurse = new ArrayList<>();
public Dozent(String name, int alter, String mitarbeiterNr, double gehalt, String fachbereich) {
super(name, alter, mitarbeiterNr, gehalt);
this.fachbereich = fachbereich;
}
@Override
public String getInfo() {
return "Dozent: " + name + " (" + mitarbeiterNr + "), Fachbereich: " + fachbereich;
}
@Override
public void arbeiten() {
System.out.println(name + " arbeitet im Fachbereich " + fachbereich);
}
public void vorlesungHalten(Kurs kurs) {
unterrichteteKurse.add(kurs);
kurs.setDozent(this);
System.out.println(name + " hält Vorlesung: " + kurs.getTitel());
}
public void klausurKorrigieren(Pruefung pruefung) {
System.out.println(name + " korrigiert Klausur für " + pruefung.getStudent().getName());
pruefung.noteEintragen(Math.random() * 4 + 1); // Random grade 1-5
}
// Getter
public String getFachbereich() { return fachbereich; }
public List<Kurs> getUnterrichteteKurse() { return new ArrayList<>(unterrichteteKurse); }
}
public class Verwaltungsmitarbeiter extends Mitarbeiter {
private String abteilung;
public Verwaltungsmitarbeiter(String name, int alter, String mitarbeiterNr, double gehalt, String abteilung) {
super(name, alter, mitarbeiterNr, gehalt);
this.abteilung = abteilung;
}
@Override
public String getInfo() {
return "Verwaltungsmitarbeiter: " + name + " (" + mitarbeiterNr + "), Abteilung: " + abteilung;
}
@Override
public void arbeiten() {
System.out.println(name + " arbeitet in der Verwaltung: " + abteilung);
}
public void dokumenteVerwalten() {
System.out.println(name + " verwaltet Dokumente in " + abteilung);
}
// Getter
public String getAbteilung() { return abteilung; }
}
// Additional classes
public class Kurs {
private String titel;
private int credits;
private Dozent dozent;
private List<Student> studenten = new ArrayList<>();
private List<Pruefung> pruefungen = new ArrayList<>();
public Kurs(String titel, int credits) {
this.titel = titel;
this.credits = credits;
}
public void addStudent(Student student) {
studenten.add(student);
}
public void setDozent(Dozent dozent) {
this.dozent = dozent;
}
public void addPruefung(Pruefung pruefung) {
pruefungen.add(pruefung);
}
// Getter
public String getTitel() { return titel; }
public int getCredits() { return credits; }
public Dozent getDozent() { return dozent; }
public List<Student> getStudenten() { return new ArrayList<>(studenten); }
}
public class Pruefung {
private Date datum;
private double note;
private Student student;
private Kurs kurs;
public Pruefung(Student student, Kurs kurs) {
this.student = student;
this.kurs = kurs;
this.datum = new Date();
this.note = 0.0; // Not yet graded
}
public void noteEintragen(double note) {
this.note = note;
student.addPruefung(this);
kurs.addPruefung(this);
}
public boolean bestanden() {
return note > 0 && note <= 4.0;
}
// Getter
public Date getDatum() { return datum; }
public double getNote() { return note; }
public Student getStudent() { return student; }
public Kurs getKurs() { return kurs; }
}
public class Raum {
private String nummer;
private int kapazitaet;
private boolean gebucht = false;
public Raum(String nummer, int kapazitaet) {
this.nummer = nummer;
this.kapazitaet = kapazitaet;
}
public void reservieren() {
gebucht = true;
System.out.println("Raum " + nummer + " reserviert");
}
public boolean istGebucht() {
return gebucht;
}
// Getter
public String getNummer() { return nummer; }
public int getKapazitaet() { return kapazitaet; }
}
}
Best Practices for UML Class Diagrams
1. Consistent Naming Conventions
// Gute Namenskonventionen
public class NamingConventions {
// Klassen: Substantive, PascalCase
public class StudentManagementSystem {}
// Methoden: Verben, camelCase
public void calculateGrade() {}
public void validateInput() {}
// Variablen: camelCase, beschreibend
private List<Student> enrolledStudents;
private double averageGrade;
// Konstanten: UPPER_CASE
public static final int MAX_STUDENTS_PER_COURSE = 100;
// Interfaces: Adjektive oder Fähigkeiten, PascalCase
public interface Printable {}
public interface Serializable {}
public interface StudentRepository {}
}
2. Separating Responsibilities
@startuml
' Gute Aufteilung nach Verantwortlichkeiten
class UserRepository {
+save(user: User): void
+findById(id: String): User
+findAll(): List<User>
+delete(id: String): void
}
class UserService {
-userRepository: UserRepository
-emailService: EmailService
+registerUser(userData: UserData): User
+authenticateUser(username: String, password: String): boolean
+updateUserProfile(userId: String, profile: UserProfile): void
}
class EmailService {
+sendWelcomeEmail(user: User): void
+sendPasswordReset(user: User): void
}
' Klare Abhängigkeiten
UserService ..> UserRepository : verwendet >
UserService ..> EmailService : verwendet >
@enduml
3. Avoiding Circular Dependencies
@startuml
' Schlecht: Zyklische Abhängigkeiten
class A {
-b: B
+doSomething(): void
}
class B {
-c: C
+doSomethingElse(): void
}
class C {
-a: A
+doAnotherThing(): void
}
A --> B
B --> C
C --> A ' Zyklus!
' Gut: Keine Zyklen
class GoodA {
-service: CommonService
+doSomething(): void
}
class GoodB {
-service: CommonService
+doSomethingElse(): void
}
class GoodC {
-service: CommonService
+doAnotherThing(): void
}
class CommonService {
+sharedOperation(): void
}
GoodA --> CommonService
GoodB --> CommonService
GoodC --> CommonService
@enduml
Exam-Relevant Concepts
Important Relationship Types
| Relationship Type | Symbol | Meaning | Lifespan |
|---------------|--------|-----------|-------------|
| Association | ---- | Structural relationship | Independent |
| Aggregation | o-- | "has-a" (partial) | Independent |
| Composition | *-- | "has-a" (whole) | Dependent |
| Dependency | ..> | "uses" | Temporary |
| Inheritance | <\|-- | "is-a" | Permanent |
| Implementation | ..\|> | "implements" | Permanent |
Typical Exam Tasks
- Draw class diagrams for given scenarios
- Identify relationship types
- Implement relationships in Java
- Refactor poor designs
- Explain differences between aggregation and composition
Summary
UML class diagrams are essential for software architecture:
- Association: Structural relationships between classes
- Aggregation: “has-a” relationship, parts can exist independently
- Composition: “has-a” relationship, parts are dependent
- Dependency: Use of classes or interfaces
- Inheritance: “is-a” relationship between classes
- Implementation: Interface realization
Good class design follows SOLID principles and avoids circular dependencies.