UML Klassendiagramm – So behältst du bei der Objektorientierung den Überblick!

 Keylearnings:

  • Wie du Beziehungen zwischen Klassen mit einem UML Klassendiagramm darstellst.
  • Was ist bei einem objektorientierten Design zu beachten?
  • Wie du Attribute und Methoden im Klassendiagramm darstellst.
  • Was ist Multiplizität?
  • Wie du Beziehungen zwischen Klassen darstellst.
  • Was ist der Unterschied zwischen Aggregation und Komposition?
  • Wie du Vererbung im UML Klassendiagramm darstellst.

Wir wissen es bereits.

Objektorientierte Programmierung ist der Himmel!

Außer du machst es falsch, dann landest du in der Hölle!

Und dahin wollen wir ganz sicher nicht!

Bevor du in die Tasten haust solltest du dir also unbedingt ein paar Gedanken machen.

Gerne kannst du das im Garten bei einer kühlen Cola tun. Hauptsache du hast einen Schreibblock und einen Bleistift mit Radiergummi zur Hand.

Oder du verwendest die kostenlose Software UMLet.

Denn ein großer Vorteil des objektorientierten Designs ist, dass man die Komponenten und deren Zusammenhänge in einem Softwaresystem grafisch darstellen kann.

So wie wir es bereits hier mit einer Klasse Vierbeiner gemacht haben.

 

Mehrfachvererbung mit java interfaces

Eine sehr smarte Möglichkeit der visuellen Darstellung von Klassen und deren Zusammenhänge ist das sogenannte UML Klassendiagramm. Hierbei steht UML für Unified Modeling Language.

Das Klassendiagramm ist ein Tool, das du dringend in dein Werkzeugkasten aufnehmen solltest.

Wie jedes Werkzeug kannst du allerdings auch das UML Klassendiagramm erst effektiv nutzen, wenn du deren Einsatzgebiet verstehst.

Deshalb wollen wir uns zunächst darüber unterhalten, über welche Dinge wir uns bei einem objektorientierten Design den Kopf zerbrechen müssen.

Objektorientiertes Design mit dem UML Klassendiagramm

Eine Klasse besteht aus drei Bestandteilen. Jede Klasse hat einen Namen, Eigenschaften (auch Attribute genannt) und Methoden.

Aus Klassen erzeugen wir konkrete Objekte (instanziieren). So wird beispielsweise aus der Klasse Hund eine Hunde-Instanz mit dem Namen Snoopy und einem Gewicht von 20kg.

Die Attribute der Klasse beschreiben hierbei den Zustand des Objekts, wie z.B. Name und Gewicht eines Hundes.

Methoden beschreiben hingegen das Verhalten eines Objekts und geben ihm Fähigkeiten, wie z.B. dass ein Hund bellen kann.

Im UML Klassendiagramm werden diese drei Elemente durch waagerechte Striche voneinander getrennt. Für unser Hunde Beispiel sieht das Klassendiagramm wie folgt aus:

UML Klassendiagramm Hund

Ganz oben steht der Name der Klasse. In unserem Fall heißt die Klasse Hund.

Der mittlere Teil enthält die Klassen-Attribute. Also den Namen und das Gewicht des Hundes.

Jedes Attribut hat einen Datentyp, den man durch einen Doppelpunkt getrennt hinter den jeweiligen Attributs-Namen schreibt.

Die Methoden werden samt Parameterliste und Rückgabewert im unteren Teil des UML Klassendiagramms aufgeführt wobei der Datentyp des Rückgabewerts hinter dem Doppelpunkt steht.

Wir haben es hier mit einem ziemlich einfach gestrickten Hund zu tun. Neben der Methode bellen enthält unsere Klasse lediglich die getter- und setter- Methoden für die Attribute.

Ist da nicht noch was?

Bestimmt ist es dir schon aufgefallen!

Wir haben den Attributen ein Minuszeichen – und den Methoden ein +Pluszeichen vorangestellt.

Wie du aus den Grundlagen der objektorientierten Programmierung weißt, sollten Instanz-Variablen, um diese gegen Manipulation zu schützen, von Außen nicht sichtbar, also als privat deklariert sein.

Der Fachmann spricht hierbei von Kapselung.

Und genau dafür steht das Minuszeichen – . Ein Attribut oder einer Methode, der wir ein Minuszeichen voranstellen ist privat deklariert, wohingegen das Pluszeichen + für ein als public deklariertes Attribut bzw. eine als public definierte Methode steht.

Selbstverständlich ist es auch möglich Attribute und Methoden als protected zu definieren.

Als protected deklarierte Attribute bzw. definierte Methoden sollen nur innerhalb der Klasse selbst und allen Unterklassen sichtbar sein.

Im UML Klassendiagramm kennzeichnen wir die Sichtbarkeit protected mit Hilfe des Hashzeichen #.

Klassenvariablen im UML Klassendiagramm

Bisher handelte es sich bei unseren Attributen immer um Instanz-variablen. Jede Instanz unserer Hunde Klasse beansprucht also einen eigenen getrennten Speicherbereich.

Was ist aber wenn wir die Anzahl der erzeugten Hunde-Objekte zählen wollen?

Für diesen Zweck benötigen wir EINE Integer-Variable, auf die alle Hunde-Instanzen Zugriff haben.

Eine solche Variable wird Klassenvariable genannt und in Java mittels des Schlüsselworts static definiert.

Im UML Klassendiagramm werden Klassenvariablen mit Hilfe eines Unterstrichs gekennzeichnet.

Los! Lass uns das Klassendiagramm von oben um eine Klassenvariable, mit der wir die Anzahl der erzeugten Hunde zählen können ergänzen.

UML Klassendiagramm static

Das war einfach, oder? Wir mussten lediglich den Attribute-Teil des UML Klassendiagramms um eine mit Unterstrich versehende Integer-Variable hundZaehler ergänzen.

Multiplizität im UML Klassendiagramm

Bis hierhin haben wir es uns einfach gemacht. Unsere Attribute bestehen bisher nur aus primitiven Datentypen. Was machen wir aber, wenn wir Arrays oder Array-Listen verwenden wollen?

Hierfür gibt es die sogenannte Multiplizität.

Natürlich hat unser Hund drei Lieblingsspielzeuge, nämlich Frauchen, Lego und einen Baseballschläger.

Und zwar genau in dieser Reihenfolge!

Wir benötigen also ein Array, das diese drei Elemente in der angegebenen Reihenfolge aufnehmen kann.

Hierzu schreiben wir die sogenannte Multiplizität [1..3] und das Kennzeichen {order} hinter das Attribut, das die Spielzeuge aufnimmt.

UML Klassendiagramm Multiplizität

Über die Multiplizität [1..3] legen wir die Kapazität des Arrays fest. Mit dem Zusatz {order} kennzeichnen wir, dass es sich bei libelingsSpielzeug um eine geordnete Datenstruktur handelt, bei der es auf die Reihenfolge ankommt.

Außerdem findet ein stolzer Hund jeden Tag ein neues Futter, das ihm schmeckt.

Daher benötigen wir des Weiteren eine Datenstruktur, die beliebig viele Elemente aufnehmen kann. Außerdem soll jedes Futter nur ein einziges mal, d.h. eindeutig, in der Datenstruktur gespeichert werden.

Für diesen Zweck stellt die UML die Multiplizität [*] und das Kennzeichen {unique}  zur Verfügung.

Erweitern wir unser UML Klassendiagramm also ein weiteres mal.

UML_Klassendiagramm_unique

Unser Hund kann jetzt beliebig viele Lieblingsmahlzeiten haben. Allerdings kann jedes Futter nur eindeutig im Attribut lieblingsFutter gespeichert werden. Eine Konstellation wie: „Meine Lieblingsgerichte sind 1. Pizza, 2. Pizza und 3. Pizza“ ist wegen des Kennzeichens {unique} nicht möglich.

Konstanten im UML Klassendiagramm

Um die Wartbarkeit deiner Programme zu erhöhen solltest du Werte, die sich nicht ändern in Konstanten definieren.

Die berühmteste aller Konstanten ist Pi. Anstatt überall da wo wir mit der Zahl Pi rechnen 3.14.. hinzuschreiben, verwenden wir die Konstante Math.PI.

Konstanten werden in Java mit Hilfe des Schlüsselwortes final deklariert und im UML Klassendiagramm mit dem Zusatz {readOnly} versehen.

Eine oft anzutreffende Verwendung von Konstanten sind Versionsnummern. Erweitern wir unser UML Klassendiagramm also erneut.

UML_Klassendiagramm_readOnly

Da die Version der Klasse für jede Instanz des Hundes die gleiche ist, handelt es sich bei der Variablen VERSION um eine Klassenvariable, die im Klassendiagramm unterstrichen dargestellt werden muss.

Vom UML Klassendiagramm zum Programmcode

Ziel unserer Anstrengungnen ist ein lauffähiges Programm.

Alle unsere bisherigen Bemühungen bringen uns nur etwas, wenn wir das Klassendiagramm möglichst leicht in Java Quellcode übersetzen können.

Und genau hierum wollen wir uns als nächstes kümmern.

Hier der aus dem Klassendiagramm erzeugte Quellcode.

1:public class Hund {
	
2:	private String name = null;
3:	private int gewicht = 0;
	
4:	private ArrayList lieblingsSpielzeug = new ArrayList();
5:	private HashSet libelingsFutter = new HashSet();
	
6:	private static int hundeZaehler = 0;
7:	private static final String VERSION = "2.2.1";
	
8:	public void bellen(){}

9:	public String getName() {}

10:	public void setName(String name) {}

11:	public int getGewicht() {}

12:	public void setGewicht(int gewicht) {}
	
}

In den Zeilen zwei und drei deklarieren wir die primitiven Attribute name und gewicht.

Anschließend verwenden wir eine Array Liste um die Lieblingsspielzeuge, und ein HashSet um die Lieblingsspeisen unseres Hundes zu speichern.

Hierbei verwenden wir ein HashSet, da wir wegen der Markierung durch {unique} die Mahlzeiten eindeutig in unserer Datenstruktur abspeichern müssen.

Zu guter letzt fügen wir in den Zeilen sechs und sieben noch die statische Zählervariable hundZaehler und die Konstante VERSION als Attribute hinzu.

In den Zeilen acht bis zwölf sind die Methoden aufgeführt.

Hieraus wird auch klar was das UML Klassendiagramm NICHT leisten kann. Das Klassendiagramm beschreibt lediglich welche Methoden eine Klasse zur Verfügung stellt. Es liefert aber keinen Hinweis darauf wie die Funktionalität dieser Methoden implementiert werden muss.

Das Klassendiagramm hilft uns also nicht dabei einen Algorithmus zu modellieren. Für diesen Zweck stellt die UML allerdings andere Diagramme wie beispielsweise das Sequenzdiagramm zur Verfügung.

So stellst du Beziehungen im UML Klassendiagramm dar

Ganz ehrlich! Bisher ist das alles nur lästig und bringt überhaupt nichts.

Eine einzelne Klasse Hund, hättest du auch ohne den ganzen Aufwand in den Rechner hämmern können.

Interessant wird es erst, wenn unser Softwaresystem aus mehr als nur einer einzelnen Klasse besteht und wir beschreiben möchten wie diese Klassen miteinander in Verbindung stehen.

Genau wie es im echten Leben, freundschaftliche, romantische oder geschäftliche Beziehungen gibt, gibt es auch in der Objektorientierung verschiedene Beziehungsarten.

Beginnen wir mit der ersten Art, nämlich den Abhängigkeiten, die in der Fachliteratur häufig auch Dependencies genannt werden.

Dependencies im UML Klassendiagramm

Ein Hund hat Hunger und frisst aus einem Fressnapf.

Der Fressnapf ist ein Objekt, das wir aus einer Klasse Fressnapf erzeugen und fressen ist eine Methode der Klasse Hund mit einer Fressnapf-Instanz als Parameter.

Die Fressnapf-Instanz ist kein fester Bestandteil des Hundes, sondern wird nur solange verwendet bis die Methode fressen abgearbeitet wurde.

Eine solche Beziehung wird Verwendungsbeziehung genannt und wird im UML Klassendiagramm mit Hilfe eines mit dem Merkmal <use> beschrifteten Pfeils dargestellt.

UML Klassendiagramm Verwendungsbeziehung

UML Assoziationen

Warst du schon einmal in einem Tierheim?

In einem Tierheim gibt es Tiere (wer hätte das gedacht), Kaninchen, Katzen, Mäuse und auch Hunde, um die sich ein Tierpfleger kümmert.

Zwischen Hund und Tierpfleger besteht innerhalb eines Tierheims ganz offensichtlich eine Beziehung.

Allerdings ist die Beziehung nicht so stark, dass der eine nicht ohne den anderen könnte.

Sowohl ein Tierpfleger als auch ein Hund kann eigenständig existieren.

Ein Hund kann glücklich in eine Familie integriert sein und es gibt Tierpfleger, die sich nur um Knut den Eisbären kümmern.

Solch schwache Verbindungen werden mit Hilfe einer einfachen Verbindungslinie zwischen den Klassen dargestellt.

UML Klassendiagramm Assoziation

UML Aggregationen und Kompositionen

Häufig haben wir es mit Klassen zu tun, die als Attribute Instanzen anderer Klassen enthalten.

So besitzt beispielsweise das Tierheim Instanzen eines Tierpflegers und eines Hundes.

Allerdings ist auch hier wieder wichtig, dass sowohl der Pflegehund als auch der Tierpfleger ohne das Tierheim existieren können.

Der Pflegehund ist ohne das Tierheim ein noch ärmerer Hund und der Tierpfleger ist ohne Tierheim ein arbeitsloser Tierpfleger.

Aber beide sind nach wie vor existent. Eine solche Beziehung heißt Aggregation und wird mit einem Diamantenzeichen im UML Klassendiagramm gekennzeichnet.

UML_Klassendiagramm_Aggregation

Die Komposition!

Eine stärkere Assoziation ist die sogenannte Komposition.

Bei diesem Assoziationstyp ist die Beziehung so stark, dass mit dem Löschen des „Behälterobjekts“ auch das integrierte Objekt verschwindet.

Genau das ist bei unserem Fressnapf der Fall!

Denn werfen wir den mit Futter gefüllten Fressnapf weg, verlieren wir auch das darin enthaltene Futter.

Eine Komposition kennzeichnen wir mit einem ausgefüllten Diamantzeichen.

UML Klassendiagramm Komposition

Wie unterscheiden sich Aggregation und Komposition in der Implementierung?

Entscheidender Unterschied zwischen Aggregation und Komposition ist die stärke der enthält Beziehung.

Schauen wir uns das an einem Beispiel an.

Hund bello = new Hund();
Tierheim heim = new Tierheim(bello);

Hier erzeugen wir eine Hunde-Instanz bello, die wir über den Konstruktor der Klasse Tierheim in ein Heim einquartieren.

Was passiert mit bello, wenn wir die Tierheim Instanz löschen?

Nix! Denn die Instanz bello liegt in einem eigenen Speicherbereich, der unabhängig von dem Bereich, in welchem das Tierheim liegt ist.

Die Tierheim Instanz enthält lediglich eine Referenz auf das Objekt bello.

Daher handelt es sich in diesem Fall um eine Aggregation.

Werfen wir einen Blick auf die Komposition.

Fressnapf napf = new Fressnapf(new Futter());

Hier erzeugen wir einen mit Futter gefüllten Fressnapf.

Das Futter erzeugen wir im Argument des Fressnapf Konstruktors, weshalb das Futter in dem für den Fressnapf reservierten Speicherbereich liegt.

Was passiert also mit dem Futter, wenn wir die napf Instanz löschen?

Korrekt! Mit dem Napf verlieren wir auch das Futter, weshalb es sich in diesem Fall um eine Komposition handelt.

Vererbung im UML Klassendiagramm

Vermisst du noch was? Ja, ich auch!

Keine objektorientierte Programmierung ohne Vererbung!

Ein Hund ist ein Vierbeiner, genau wie eine Katze oder ein Elefant. Daher führen wir eine Klasse Vierbeiner ein, in der wir die allgemeinen Eigenschaften eines Vierbeiners implementieren und davon die Tiere Hund, Katze etc. ableiten.

Im UML Klassendiagramm wird Vererbung mit Hilfe eines Pfeils dargestellt.

UML Klassendiagramm Vererbung

Vierbeiner ist Oberklasse des Hundes, in der wir die Methoden und Eigenschaften implementieren, die alle Vierbeiner gemeinsam haben.

Das Vierbeiner eine Oberklasse des Hundes ist, deuten wir mit einem zu der Klasse Vierbeiner gerichteten Pfeil an.

Theorie und Praxis

Die hier beschriebene Vorgehensweise wird Wasserfallmodell genannt.

Beim Wasserfallmodell setzten wir voraus, dass wir alle Anforderungen von Beginn an kennen und gehen außerdem davon aus, dass sich diese während des gesamten Entwicklungsprozesses nicht ändern.

In der Praxis ist diese Voraussetzung leider oft nicht erfüllt, weshalb mit einer iterativen Entwicklung gearbeitet wird, bei der die typischen Entwicklungsarbeiten wie Design, Implementierung und Tests parallel stattfinden.

In jedem Iterationsschritt werden die Anforderungen, die die Software erfüllen soll verfeinert.

Derzeit ist hier die Agile-Softwareentwicklung der Platzhirsch.

Zu guter letzt möchte ich dir im folgenden Video noch zeigen wie du das Klassendiagramm in Quellcode umsetzt.

Fazit: Auch wenn das Erstellen eines UML Klassendiagramms zunächst wie unnötiger Mehraufwand erscheint, ist es in Wirklichkeit so, dass du hiermit wertvolle Vorarbeit leistest, die dir während der Implementierung viele Fehlerkorrekturen erspart.

Hast du auch schon mit dem UML Klassendiagramm gearbeitet? Was ist deine Erfahrung? Hinterlass mir doch einfach einen Kommentar!

Hat dir der Artikel gefallen? Dann folge uns am besten gleich auf Facebook!

Hallo ich bin Kim und ich möchte ein großer Programmierer werden. Machst du mit?

Kommentare (26)

  • Antworte

    Hallo Kim, danke für deine ausführlichen Berichte. Ich lese immer wieder gerne hier. Mach weiter so!

    Gruß

    Bob

    • Vielen Dank Bob!

  • Antworte

    Kim Peter, bin in der Schule auf deine Seite gestoßen, finde sie mega

    • Vielen Dank! 🙂

  • Antworte

    Hallo Herr Peter, ihr Bericht hat mir schon sehr weitergeholfen. Mich würde nur interessieren, wie ich bei den Klassen die Unterstruche für die jeweiligen Attribute mache.

    • Hallo Maxim, die Unstriche bedeuten einfach nur, dass es sich um eine Klassenvariable handelt. Also Variablen, die mit Schlüsselwort static definiert sind. Viele Grüße Kim

  • Antworte

    Hey Kim! Einen Tag vor der Klausur OOP mit C++ bin ich über Deine Seite gestoßen. Ich hätte mir viel Mühe und Verzweiflung sparen können, wenn mir jemand UML an einem so anschaulichen Beispiel erklärt hätte, wie Du es hier getan hast. Vielen Dank für den tollen Artikel!

    • Hallo Dominik, vielen Dank. Ich hoffen deine Klausur ist gut gelaufen. Viele Grüße Kim

  • Antworte

    Hallo,

    wie wird die Vererbung im Programm implementiert? Würdest du mir bitte ein Beispiel nennen.

  • Antworte

    Hast du vielleicht ein Beispiel in Python ?
    es ist mir schwer Klassendigramme(mit verschiedenen Beziehungselemente) in Python zu setzen 🙁

    • Hallo Eya, das Thema habe ich bisher leider noch nicht im Programm. Viele Grüße Kim

  • Antworte

    Super!!

    Danke

  • Antworte

    Hallo Kim,

    ich finde Deine Seite voll supi!!!!! 😉
    Gerne lerne ich mehr von Dir!!

    Liebe Grüße

    Deine Justine

    • Hallo Justine, das freut mich sehr! Viele Grüße Kim

  • Antworte

    will nicht den grammarnazi spielen, aaaaaaber „behältst“ wär schon besser

    (nicht böse gemeint xD)

    • Absolut richtig. Habe die Überschrift geändert. Danke für den Hinweis. Viele Grüße Kim

  • Antworte

    Danke Kim, ich lerne auch für eine Prüfung(Modellierung und Design Patterns) und wie du alles hier erklärt hast finde ich einfach super verständlich!

    • Ich wünsche dir viel Erfolg für deine Prüfungen!

  • Antworte

    Du wirst sicherlich ein großartiger Programmierer, vielleicht sogar einer der besten unserer Zeit. Ich liebe deine Beiträge, und küsse deine Augen.

    • Danke! 😉

  • Antworte

    Hallo Kimm,
    bin zufällig auf deine Seite gestoßen und bin begeistert wie toll und verständlich du die einzelnen Themen erklärst.
    Werde in nächster Zeit öfters bei dir vorbei schauen.

    Vielen Dank für deine tolle Arbeit
    Gruß
    Jörg

    • Hallo Jörg, danke das freut mich. Viele Grüße Kim

  • Antworte

    Hi, super Beitrag, doch mir ist aufgefallen, dass fressen(Napf : Fressnapf) keinen Rückgabewert hat 🙂 müsste doch void sein?

    Liebe Grüße,
    Lara

    • Hallo Lara, danke und ja du hast recht. Viele Grüße Kim

Antworte auf Dominik Abbruch