Klassendiagramme
Ein Klassendiagramm ist ein Diagramm, das in der Softwareentwicklung und -modellierung verwendet wird, um Klassen und deren Beziehungen zu beschreiben. Klassendiagramme ermöglichen es uns, Software auf einem hohen Abstraktionsniveau zu modellieren, ohne den Quellcode betrachten zu müssen.
Klassen in einem Klassendiagramm entsprechen den Klassen im Quellcode. Das Diagramm zeigt die Namen und Attribute der Klassen, Verbindungen zwischen den Klassen und manchmal auch die Methoden der Klassen.
Beschreibung von Klassen und Klassenattributen
Zunächst beschreiben wir eine Klasse und deren Attribute. Unten ist der Quellcode für eine Klasse namens Person
dargestellt, die zwei Klassenattribute name
und age
hat.
public class Person {
private String name;
private int age;
}
In einem Klassendiagramm wird eine Klasse durch ein Rechteck dargestellt, wobei der Name der Klasse oben steht. Eine Linie unter dem Klassennamen trennt diesen von der Liste der Attribute (Namen und Typen der Klassenvariablen). Die Attribute werden zeilenweise aufgelistet.
In einem Klassendiagramm werden Klassenattribute als "attributeName: attributeType" notiert. Ein +
vor dem Attributnamen bedeutet, dass das Attribut öffentlich ist, und ein -
, dass es privat ist.
![[Person|-name:String;-age:int] [Person|-name:String;-age:int]](/static/d48bf1d9bb02ae0562bc54f09d0c349c/d76be/part4.1-classdiagram-person-name-age.png)
Beschreibung des Klassenkonstruktors
Im Folgenden haben wir den Quellcode für einen Konstruktor der Person
-Klasse hinzugefügt. Der Konstruktor erhält den Namen der Person als Parameter.
public class Person {
private String name;
private int age;
public Person(String initialName) {
this.name = initialName;
this.age = 0;
}
}
In einem Klassendiagramm listen wir den Konstruktor (und alle anderen Methoden) unter den Attributen auf. Eine Linie unterhalb der Attributliste trennt diese von der Methodenliste.
Methoden werden mit +/-
(abhängig von der Sichtbarkeit der Methode), dem Methodennamen, den Parametern und deren Typen notiert. Der oben stehende Konstruktor wird als +Person(initialName:String)
dargestellt.
Die Parameter werden auf die gleiche Weise notiert wie die Klassenattribute: "parameterName: parameterType".
![[Person|-name:String;-age:int|+Person(initialName:String)] [Person|-name:String;-age:int|+Person(initialName:String)]](/static/2f5de848da00fe36da2f8fb405a31967/8ff5a/part4.1-classdiagram-person-name-age-constructor.png)
Beschreibung von Klassenmethoden
Im Folgenden haben wir der Person
-Klasse eine Methode printPerson()
, die void
zurückgibt, hinzugefügt.
public class Person {
private String name;
private int age;
public Person(String initialName) {
this.name = initialName;
this.age = 0;
}
public void printPerson() {
System.out.println(this.name + ", age " + this.age + " years");
}
}
In einem Klassendiagramm werden alle Klassenmethoden einschließlich der Konstruktoren aufgelistet; Konstruktoren werden zuerst und dann alle Klassenmethoden aufgelistet. Wir schreiben auch den Rückgabetyp einer Methode im Klassendiagramm.
![[Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void] [Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void]](/static/215eec54786bd7d3a2e70d1e54ec9e09/8ff5a/part4.1-classdiagram-person-name-age-constructor-print.png)
Im Folgenden haben wir der Person
-Klasse die Methode getName
hinzugefügt, die den Namen der Person zurückgibt.
public class Person {
private String name;
private int age;
public Person(String initialName) {
this.name = initialName;
this.age = 0;
}
public void printPerson() {
System.out.println(this.name + ", age " + this.age + " years");
}
public String getName() {
return this.name;
}
}
![[Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String] [Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String]](/static/a3672305b4b34e5d6d5d28b4c85fd7e6/8ff5a/part11.1-classdiagram-person-age-name-constructor-print-getName.png)
Verbindungen zwischen Klassen
In einem Klassendiagramm werden die Verbindungen zwischen Klassen durch Pfeile dargestellt. Die Pfeile zeigen auch die Richtung der Verbindung an.
Im Folgenden haben wir eine Klasse Book
.
public class Book {
private String name;
private String publisher;
// constructors and methods
}
![[Book|-name:String;-publisher:String] [Book|-name:String;-publisher:String]](/static/a98e907f91d0ff7cf8eb8211fac9af4b/69538/part11.1-classdiagram-book-name-and-publisher.png)
Wenn wir dem Quellcode eine Variable author
, deren Typ Person
ist, hinzufügen, wird die Variable wie alle anderen Klassenvariablen deklariert.
public class Book {
private String name;
private String publisher;
private Person author;
// constructors and methods
}
In einem Klassendiagramm werden Variablen, die auf andere Objekte verweisen, nicht zusammen mit den anderen Klassenattributen notiert, sondern als Verbindungen zwischen den Klassen dargestellt. Im untenstehenden Klassendiagramm haben wir die Klassen Person
und Book
sowie die Verbindung zwischen ihnen.
![[Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String][Book]-author->[Person] [Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String][Book]-author->[Person]](/static/ac80fd95fffabd2071ba05f12908f473/a4078/part11.1-classdiagram-book-name-and-publisher-and-author.png)
Der Pfeil zeigt die Richtung der Verbindung an. Die oben gezeigte Verbindung zeigt, dass ein Book
seinen Autor kennt, eine Person
jedoch nichts über die Bücher weiß, deren Autor sie ist.
Wir können dem Pfeil auch eine Beschriftung hinzufügen, um die Verbindung zu beschreiben. Im obigen Diagramm ist dem Pfeil eine Beschriftung hinzugefügt, die uns mitteilt, dass ein Book
einen Autor hat.
Wenn ein Buch mehrere Autoren hat, werden die Autoren in einer Liste gespeichert.
public class Book {
private String name;
private String publisher;
private ArrayList<Person> authors;
// constructors and methods
}
In einem Klassendiagramm wird diese Situation durch das Hinzufügen eines Sterns am Ende des Pfeils, der die Verbindung zwischen den Klassen zeigt, beschrieben. Der Stern sagt uns, dass ein Book
zwischen 0 und einer unbegrenzten Anzahl von Autoren haben kann. Im folgenden Diagramm haben wir keine Beschriftung hinzugefügt, um die Multiplizität der Verbindung zu beschreiben, aber es wäre aus Gründen der Klarheit eine gute Idee.
![[Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String][Book]-*->[Person] [Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String][Book]-*->[Person]](/static/06d6d57af5c9a2f840190279e36abc1a/a4078/part11.1-classdiagram-book-name-and-publisher-and-authors.png)
Klassenmethoden werden genauso beschrieben wie zuvor. Unten haben wir der Book
-Klasse die Methoden getAuthors
und addAuthor
hinzugefügt.
public class Book {
private String name;
private String publisher;
private ArrayList<Person> authors;
// constructor
public ArrayList<Person> getAuthors() {
return this.authors;
}
public void addAuthor(Person author) {
this.authors.add(author);
}
}
![[Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String|+getAuthors():ArrayList;+addAuthor(author:Person)][Book]-*->[Person] [Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String|+getAuthors():ArrayList;+addAuthor(author:Person)][Book]-*->[Person]](/static/c2bb881cb6bd26924121044b7f4ab78c/508ef/part11.1-classdiagram-book-name-and-publisher-and-authors-and-methods.png)
Wir könnten den Typ der Elemente in der ArrayList ArrayList<Person>
und eine Beschriftung, die die Verbindung beschreibt, "authors" in das obige Klassendiagramm aufnehmen.
Wenn keine Pfeilspitze in einer Verbindung vorhanden ist, wissen beide Klassen voneinander. Unten ist ein Beispiel, bei dem ein Buch über seinen Autor Bescheid weiß und eine Person über ein Buch, das sie geschrieben hat.
![[Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String|+getAuthors():ArrayList;+addAuthor(author:Person)][Book]-*[Person] [Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String|+getAuthors():ArrayList;+addAuthor(author:Person)][Book]-*[Person]](/static/a20c65ebd60567175c0e7c92b031e031/508ef/part11.1-classdiagram-book-person-bidirectional.png)
public class Person {
private String name;
private int age;
private Book book;
// ...
}
public class Book {
private String name;
private String publisher;
private ArrayList<Person> authors;
// ..
}
Wie Sie sehen können, ist die Verbindung standardmäßig — wenn kein Stern auf der Verbindung vorhanden ist — singulär. Die oben dargestellten Klassen sind interessant, da eine Person
nur ein Book
haben kann.
Wenn eine Person mehrere Bücher haben kann und ein Buch mehrere Autoren haben kann, fügen wir an beiden Enden der Verbindung einen Stern hinzu:
![[Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String|+getAuthors():ArrayList;+addAuthor(author:Person)][Book]*-*[Person] [Person|-name:String;-age:int|+Person(initialName:String);+printPerson():void;+getName():String][Book|-name:String;-publisher:String|+getAuthors():ArrayList;+addAuthor(author:Person)][Book]*-*[Person]](/static/8b665c3efb781ae947114b2a10d796da/508ef/part11.1-classdiagram-book-person-bidirectional-many-to-many.png)
Nun würde die Person
-Klasse wie folgt aussehen:
import java.util.ArrayList;
public class Person {
private String name;
private int age;
private ArrayList<Book> books;
// ...
}
Beschreibung von Vererbung
In einem Klassendiagramm wird Vererbung durch einen Pfeil mit einem dreieckigen Kopf beschrieben. Das Dreieck zeigt auf die Klasse, von der geerbt wird. Im folgenden Beispiel erbt die Klasse Engine
von der Klasse Part
.
![[Part|-id:String;-manufacturer:String;-description:String][Engine|-type:String][Part]^-[Engine] [Part|-id:String;-manufacturer:String;-description:String][Engine|-type:String][Part]^-[Engine]](/static/6ddb1e56e326b33318cc67e5aa33b0c1/4dcb9/part11.1-classdiagram-engine-inherits-part.png)
Im folgenden Beispiel beschreibt das Klassendiagramm die Klassen aus der Übung ProductWarehouseWithHistory
. Die Klasse ProductWarehouseWithHistory
erbt von der Klasse ProductWarehouse
, die wiederum von der Klasse Warehouse
erbt.
ChangeHistory
ist eine separate Klasse, die mit ProductWarehouse
verbunden ist. ProductWarehouseWithHistory
kennt die ChangeHistory
, aber die ChangeHistory
kennt ProductWarehouseWithHistory
nicht.
![[Warehouse|-capacity:double;-balance:double|+Warehouse(capacity:double);+getBalance():double;+getCapacity():double;+howMuchSpaceLeft():double;+addToWarehouse(amount:double):void;+takeFromWarehouse(amount:double):double;+toString():String][ProductWarehouse|-name:String|+ProductWarehouse(name:String، capacity:double);+getName():String;+setName(name:String):String;+toString():String][ChangeHistory|-states:ArrayList|+ChangeHistory();+add(status:double);+clear():void;...][ProductWarehouseWithHistory||+ProductWarehouseWithHistory(name:String، capacity:double، initialBalance:double);+history():String;+printAnalysis():void;+addToWarehouse(amount:double);+takeFromWarehouse(amount:double):double][Warehouse]^-[ProductWarehouse][ProductWarehouse]^-[ProductWarehouseWithHistory][ChangeHistory]<-[ProductWarehouseWithHistory] [Warehouse|-capacity:double;-balance:double|+Warehouse(capacity:double);+getBalance():double;+getCapacity():double;+howMuchSpaceLeft():double;+addToWarehouse(amount:double):void;+takeFromWarehouse(amount:double):double;+toString():String][ProductWarehouse|-name:String|+ProductWarehouse(name:String، capacity:double);+getName():String;+setName(name:String):String;+toString():String][ChangeHistory|-states:ArrayList|+ChangeHistory();+add(status:double);+clear():void;...][ProductWarehouseWithHistory||+ProductWarehouseWithHistory(name:String، capacity:double، initialBalance:double);+history():String;+printAnalysis():void;+addToWarehouse(amount:double);+takeFromWarehouse(amount:double):double][Warehouse]^-[ProductWarehouse][ProductWarehouse]^-[ProductWarehouseWithHistory][ChangeHistory]<-[ProductWarehouseWithHistory]](/static/c16eee8469defdd71f61f22c7e12ec2c/99272/part11.1-classdiagram-productWarehouseWithHistory.png)
Vererbung von abstrakten Klassen wird fast genauso beschrieben wie die von regulären Klassen. Allerdings fügen wir über dem Namen der Klasse die Beschreibung <<abstract>>
hinzu. Der Name der Klasse und ihre abstrakten Methoden werden ebenfalls kursiv geschrieben.

Beschreibung von Schnittstellen
In Klassendiagrammen werden Schnittstellen als <<interface>>
NameOfTheInterface notiert. Im Folgenden beschreiben wir eine Schnittstelle Readable
.
public interface Readable {
}
![[<<interface>> Readable] [<<interface>> Readable]](/static/1fafa657c1965afe4d181c782b458fc3/0af15/part11.1-classdiagram-interface-readable.png)
Methoden werden genauso beschrieben wie bei einer Klasse.
Die Implementierung einer Schnittstelle wird durch einen gestrichelten Pfeil mit einem dreieckigen Kopf dargestellt. Im Folgenden beschreiben wir eine Situation, in der Book
die Schnittstelle Readable
implementiert.
![[<<interface>> Readable][Book]-.-^[<<interface>> Readable] [<<interface>> Readable][Book]-.-^[<<interface>> Readable]](/static/a00a51d41476ce82523cb299fe66e450/24c7e/part11.1-classdiagram-book-implements-readable.png)