UML Klassendiagramme: Beziehungen, Association, Aggregation & Komposition
UML Klassendiagramme sind das wichtigste Werkzeug zur Visualisierung von objektorientierten Softwarearchitekturen. Beziehungen zwischen Klassen definieren die Struktur und das Verhalten des Systems.
Grundlagen der UML-Klassendiagramme
Klassendarstellung
Einfache Klasse mit 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
Sichtbarkeitsmodifizierer
| Symbol | Bedeutung | Beschreibung |
|---|---|---|
+ | public | Von überall zugreifbar |
- | private | Nur innerhalb der Klasse |
# | protected | Innerhalb der Klasse und Unterklassen |
~ | package | Nur innerhalb des Pakets |
Beziehungsarten in UML
1. Assoziation (Association)
Assoziationen beschreiben strukturelle Beziehungen zwischen Klassen.
@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-Implementierung von Assoziationen
public class AssociationExamples {
// Viele-zu-viele Assoziation
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);
}
// Getter und Setter
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);
}
// Getter und Setter
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);
}
// Getter und Setter
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);
}
// Getter und Setter
public String getName() { return name; }
public Student getLeiter() { return leiter; }
}
}
2. Aggregation
Aggregation ist eine spezielle Form der Assoziation, bei der ein Teil-Objekt auch ohne das Ganze existieren kann.
@startuml
class Abteilung {
-name: String
+getName(): String
}
class Mitarbeiter {
-name: String
-position: String
+getName(): String
}
' Aggregation (leere Raute)
Abteilung o-- "*" Mitarbeiter : hat >
class Universitaet {
-name: String
+getName(): String
}
class Fakultaet {
-name: String
+getName(): String
}
' Aggregation
Universitaet o-- "*" Fakultaet : besitzt >
@enduml
Java-Implementierung von Aggregation
public class AggregationExamples {
// Abteilung kann ohne Mitarbeiter existieren
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);
}
// Mitarbeiter können auch ohne Abteilung existieren
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;
}
}
// Mitarbeiter existieren unabhängig von Abteilung
public String getName() { return name; }
public Abteilung getAbteilung() { return abteilung; }
}
}
3. Komposition (Composition)
Komposition ist eine stärkere Form der Aggregation, bei der das Teil-Objekt ohne das Ganze nicht existieren kann.
@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
}
' Komposition (gefüllte Raute)
Auto *-- "1" Motor : hat >
Auto *-- "4" Reifen : hat >
class Haus {
-adresse: String
+wohnen(): void
}
class Zimmer {
-flaeche: double
-typ: String
+reinigen(): void
}
' Komposition
Haus *-- "*" Zimmer : hat >
@enduml
Java-Implementierung von Komposition
public class CompositionExamples {
// Motor existiert nur im Auto
public class Auto {
private String marke;
private String modell;
private Motor motor; // Existiert nur mit Auto
private List<Reifen> reifen = new ArrayList<>(); // Existieren nur mit Auto
public Auto(String marke, String modell) {
this.marke = marke;
this.modell = modell;
this.motor = new Motor(200, "Diesel"); // Motor wird im Auto erstellt
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");
}
// Getter
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 - nur von Auto aufrufbar
private Motor(int leistung, String typ) {
this.leistung = leistung;
this.typ = typ;
}
public void starten() {
System.out.println("Motor (" + typ + ", " + leistung + " PS) gestartet");
}
// Getter
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; // Standarddruck
}
public void aufpumpen(double druck) {
this.druck = druck;
System.out.println("Reifen auf " + druck + " bar aufgepumpt");
}
// Getter
public int getGroesse() { return groesse; }
public double getDruck() { return druck; }
}
}
4. Abhängigkeit (Dependency)
Abhängigkeiten zeigen Verwendungsbeziehungen zwischen Klassen.
@startuml
class Bestellung {
-datum: Date
-gesamtsumme: double
+berechneGesamtsumme(): double
}
class PreisRechner {
+berechnePreis(produkte: List<Produkt>): double
+berechneRabatt(betrag: double, rabatt: double): double
}
' Abhängigkeit (gestrichelte Linie)
Bestellung ..> PreisRechner : verwendet >
class Logger {
+logInfo(nachricht: String): void
+logError(nachricht: String): void
}
class DatenbankService {
+speichern(daten: Object): void
+laden(id: String): Object
}
Bestellung ..> Logger : loggt >
Bestellung ..> DatenbankService : speichert >
@enduml
Java-Implementierung von Abhängigkeiten
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); }
}
// Abhängigkeits-Klassen
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. Vererbung (Inheritance)
Vererbung zeigt “ist-ein” Beziehungen zwischen Klassen.
@startuml
' Abstrakte Basisklasse
abstract class Fahrzeug {
#marke: String
#baujahr: int
+{abstract} fahren(): void
+{abstract} bremsen(): void
+getInfo(): String
}
' Konkrete Unterklassen
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-Implementierung von Vererbung
public class InheritanceExamples {
// Abstrakte Basisklasse
public abstract class Fahrzeug {
protected String marke;
protected int baujahr;
public Fahrzeug(String marke, int baujahr) {
this.marke = marke;
this.baujahr = baujahr;
}
// Abstrakte Methoden
public abstract void fahren();
public abstract void bremsen();
// Konkrete Methode
public String getInfo() {
return marke + " aus " + baujahr;
}
// Getter
public String getMarke() { return marke; }
public int getBaujahr() { return baujahr; }
}
// Konkrete Unterklassen
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. Implementierung (Implementation)
Implementierung zeigt die Beziehung zwischen Interfaces und implementierenden Klassen.
@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-Implementierung von Interfaces
public class InterfaceExamples {
// Interfaces
public interface Fliegend {
void fliegen();
void landen();
}
public interface Schwimmend {
void schwimmen();
void tauchen();
}
// Einfache Implementierung
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; }
}
// Mehrere Interfaces implementieren
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; }
}
// Abstrakte Klasse mit 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; }
}
// Konkrete Unterklasse
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; }
}
}
Komplexe Klassendiagramme
Vollständiges Beispiel: Universitätssystem
@startuml
' Abstrakte Basisklassen
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
}
' Konkrete Klassen
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
}
' Weitere Klassen
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
}
' Beziehungen
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 >
' Komposition
Universitaet *-- "*" Fakultaet : besitzt >
@enduml
Java-Implementierung des Universitätssystems
public class UniversitySystem {
// Abstrakte Basisklasse
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; }
}
// Abstrakte Mitarbeiter-Klasse
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; }
}
// Konkrete Klassen
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); // Zufallsnote 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; }
}
// Weitere Klassen
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; // Noch nicht benotet
}
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 für UML-Klassendiagramme
1. Konsistente Namenskonventionen
// 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. Verantwortlichkeiten trennen
@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. Vermeidung von zyklischen Abhängigkeiten
@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
Prüfungsrelevante Konzepte
Wichtige Beziehungsarten
| Beziehungstyp | Symbol | Bedeutung | Lebensdauer |
|---------------|--------|-----------|-------------|
| Assoziation | ---- | Strukturelle Beziehung | Unabhängig |
| Aggregation | o-- | "hat-ein" (teilweise) | Unabhängig |
| Komposition | *-- | "hat-ein" (ganz) | Abhängig |
| Abhängigkeit | ..> | "verwendet" | Temporär |
| Vererbung | < |-- | "ist-ein" | Permanent |
| Implementierung | ..|> | "implementiert" | Permanent |
Typische Prüfungsaufgaben
- Zeichnen Sie Klassendiagramme für gegebene Szenarien
- Identifizieren Sie Beziehungsarten
- Implementieren Sie Beziehungen in Java
- Refaktorieren Sie schlechte Designs
- Erklären Sie Unterschiede zwischen Aggregation und Komposition
Zusammenfassung
UML-Klassendiagramme sind essentiell für Softwarearchitektur:
- Assoziation: Strukturelle Beziehungen zwischen Klassen
- Aggregation: “hat-ein” Beziehung, Teile können unabhängig existieren
- Komposition: “hat-ein” Beziehung, Teile sind abhängig
- Abhängigkeit: Verwendung von Klassen oder Interfaces
- Vererbung: “ist-ein” Beziehung zwischen Klassen
- Implementierung: Interface-Realisierung
Gutes Klassendesign folgt SOLID-Prinzipien und vermeidet zyklische Abhängigkeiten.