![]() |
Comelio GmbH
|
Comelio-Blog > UML > Klasse Klassendiagramme mit der UML
KlassendiagrammeEinsatzgebieteDas Einsatzgebiet der Klassendiagramme beginnt mit dem Projekt, in den meisten Fällen nach der Erstellung der Anwendungsfallanalyse und des Aktivitätsdiagramms. Es ist durchaus sinnvoll, bereits in der Analysephase mit den Klassendiagrammen zu beginnen. Hier würde man sicherlich noch nicht detailliert planen, sondern eine erste Grobplanung erstellen. Letztendlich wird während des Projekts noch einiges geändert, und das Klassendiagramm bietet eine gute Hilfestellung, um die Komplexität einer Anwendung beziehungsweise eines Teiles davon aufzunehmen und zu beschreiben. Während der Erfassung der Kundenwünsche sollen erst einmal die allgemeinen Zusammenhänge mit Hilfe des Klassendiagramms grundlegend beschrieben werden. Die Details werden erst dann hinzugefügt oder geändert, wenn die einzusetzende Technik, Technologie, ja in manchen Fällen sogar die Programmiersprache feststeht. Im ersten Schritt fehlen häufig die Datentypen, Schnittstellen und Methoden, und die Klasse erfasst auch namentlich erst einmal nur die Aufgabe, für die sie später zuständig ist. Ein Klassendiagramm enthält alle Informationen über die eigentliche Programmierung, der enthaltenen Eigenschaften und Funktionalitäten der Klassen und der Beziehung der Klassen untereinander. Das Metamodell KlassendiagrammeAbbildung 6.1: Subpackages der Klassenpakete und deren Abhängigkeiten zeigt die Unterpakete von den Klassendiagramme und deren Abhängigkeiten:
In den meisten Programmiersprachen wie C# oder Java gibt es eine allgemeine Oberklasse, die in C# und Java jeweils Object heißt. Von dieser Oberklasse erben alle Klassen, die programmiert werden. Dieses Konzept spiegelt sich auch in der UML 2.0 wider. Somit gibt es eine Oberklasse mit dem Namen Element, von der alle Elemente erben. Diese Oberklasse ist abstrakt, von ihr kann also selbst keine Instanz gebildet werden. Sie bildet stattdessen ein allgemeines Konzept, dessen konkrete Ausprägungen in Unterklassen dargestellt werden. Sie darf explizit weitere Elemente enthalten, was sich im Metamodell beschrieben wiederfindet. Die Darstellung von Element sehen Sie im Folgenden:
Zunächst einmal gibt es in der UML 2.0 den Begriff eines benennbaren Elements (NamedElement). Solch ein benennbares Element kann einen Namen haben und ist nach außen mit einer Sichtbarkeit verbunden. Eines dieser benennbaren Elemente in der UML 2.0 ist beispielsweise eine Klasse. Eine Klasse soll innerhalb eines Systems genau eine Aufgabe übernehmen. Somit übernimmt die Klasse eine Verantwortung für einen sachlogischen Aspekt des Gesamtsystems. Im Kontext sieht wie in Abbildung 6.3: RootDiagramm aus:
Sie sehen in vorheriger Abbildung, dass man jederzeit an ein Element einen Kommentar anhängen kann. Dieser Kommentar gehört zum Element selbst, was in der Abbildung anhand der Kompositionsbeziehung ersehen können. Diese Kompositionsbeziehung stellt entsprechend den Kommentar als Eigentum des Elementes dar. Dabei kann ein Element beliebig viele Kommentare enthalten, umgekehrt auch ein Kommentar beliebig vielen Elementen gehören. Es gibt beim Kommentar noch eine andere Möglichkeit, das Element anzubringen, nämlich als angeheftetes Element (Spezifikation: +annotatedElement). Hierbei kann ein Kommentar ebenfalls beliebig vielen Elementen angeheftet werden. In der Spezifikation beschrieben ist das Namespace Diagram aus Abbildung 6.4: Namespaces. Dieses steht in einer Kompositionsbeziehung zu NamedElement. Zusätzlich gibt es noch eine gerichtete Assoziation, wobei der Namespace mehrere Benannte Elemente enthalten darf.
Im Namespace können die verschiedenen Enumerationen der VisibilityKinds angebracht werden:
Schaut man einmal auf den Import, so unterscheidet man hier zwischen Elementimport und Packageimport. Wir werden später in diesem Kapitel darauf noch genauer eingehen. Die Verhältnisse sind hier ebenfalls für die Prüfungen wichtig, es handelt sich hier um eine Kompositionsbeziehung, was bedeutet, dass die importierten Packages und Elemente fest zum Namespace gehören. Ein Namespace darf immer beliebig viele Importe zulassen, umgekehrt aber gehört immer ein Import zu einem einzigen Namespace. Die Muliplizitäten sind Teil der Abbildung 6.5: Multiplizitäten:
Zunächst zu einem typisierbaren Element (Spezifikation TypedElement). Es handelt sich dabei grundsätzlich um ein benanntes Element, was Sie in Abbildung vorheriger an der Generalisierungsbeziehung ersehen können. Ein typisierbares Element hat grundsätzlich keinen oder einen Typ. Die Multiplizität, die dieses Diagramm eigentlich beschreibt, stellt definiert ein Intervall dar und deutet die Anzahl der möglichen Kardinalitäten (Spezifikation: Cardinality) an, die die Anzahl der möglichen Ausprägungen darstellt. Dazu später mehr. Eine Wertespezifikation (Spezifikation: ValueSpecification) stellt eine Spezifikation der Werte in einem Modell dar. Es handelt sich dabei um einfache formale Ausdrücke. Diese Ausdrücke können aus der Programmiersprache sein, in denen man sie beschreibt, also zum Beispiel Java, C++, OCL etc. Der Fachbegriff hierzu heißt: OpaqueExpression. Wichtig für die Prüfung: Die Angabe der Sprache kann weggelassen werden, wenn sie aus dem Kontext verständlich ist. Wenn man die Sprache angeben will, so erfolgt dies mit der Sprachangabe in geschweiften Klammern.
Literale wie in Abbildung 6.6: Expressions stellen Spezialisierungen der Klasse LiteralSpecification dar, wobei diese wiederum von ValueSpecification abgeleitet ist, wie Sie aus Abbildung ersehen können. Dabei gibt es folgende Literale:
Dies ist hier noch einmal besonders erwähnt, da es eine Frage in der Prüfung sein kann. Eine Zusicherung (Spezifikation: Constraint) ist eine Bedingung, die für ein Element erfüllt sein muss. Es kann sich dabei um einen OCL-Ausdruck handeln, zugelassen ist aber auch ein sprachlicher Ausdruck. In nachfolgender Abbildung ist das Metamodell zu den Constraints:
Eine Instancespecification, dargestellt in Abbildung 6.8: Instanzspezifikation stellt eine konkrete Ausprägung dar. Der so genannte Slot, den Sie in vorheriger Abbildung sehen, stellt die einzelnen Werte in einem Objekt dar. Grundsätzlich ist erlaubt, dass die Angaben unvollständig sind.
In Abbildung 6.9: Classes sehen Sie die Modelle für den vorheriger Classifier und die dazugehörigen Merkmale.
Der Classifier kann mehrere Eigenschaften (Spezifikation:Properties) enthalten, wobei eine Eigenschaft immer zu 0 oder einem Classifier gehört. Eine genauere Beschreibung der Classifier gibt es nachfolgend in diesem Kapitel. Ein Feature beschreibt einige Verhaltensmerkmale oder Strukturmerkmale der Objekte einer Klasse, wie Sie in der Abbildung 6.10: erkennen.
Ein StructuralFeature ist beispielsweise die Eigenschaft, die in Abbildung 6.10: Feature auch mit einem Attribut isReadOnly versehen werden kann, das angibt, dass das entsprechende Attribut nicht mehr verändert werden kann. Ein BehavioralFeature hingegen ist beispielsweise eine Operation. Hierbei geht es, wie aus dem Namen schon zu sehen, grundsätzlich um Verhalten. Es können Parameter übergeben werden, und zwar wie in Abbildung 6.10: Feature ersichtlich, anhand der Multiziplitäten beliebig viele. Der Parameter hat dabei eine Richtung und gehört BehavioralFeature, was man an der Kompositionsbeziehung erkennt. Über die Richtungen werden wir nachfolgend noch Genaueres angeben. Abbildung 6.11: Operations zeigt die Operationen und deren formale Verhältnisse und Regelungen:
Die Operation ist wiederum ein Verhaltensmerkmal und steuert auch das Verhalten. Die Syntax und den praktischen Einsatz werden wir zu einem späteren Zeitpunkt in diesem Kapitel vorstellen. Hier soll zunächst das Metamodell erläutert werden: Wie Sie sehen, gibt es grundsätzlich Vor- und Nachbedingungen. Zu den Operationen gehören per Kompositionsbeziehung die Parameter, Constraints und Typen. In der Spezifikation kommt danach die Beschreibung der Properties. Die Eigenschaften können auch per Assoziation beschrieben werden. Wie schon erwähnt, stellen die Eigenschaften ein StructuralFeature dar
Die Assoziation ist inAbbildung 6.12: Properties ebenfalls ein Thema. Zur Assoziation gehören beliebig viele Eigenschaften. Pakete, wie in beschrieben besitzen grundsätzlich jeweils beliebig viele Elemente und Typen. Später im Kapitel wird noch genau erläutert, wie der PackageMerge grundsätzlich in der Praxis verläuft.
Bei den Abhängigkeiten handelt es sich um Beziehungen von Elementen. Dabei geht es um Quellelemente (Spezifikation: +supplier) und um clients, die das Ziel darstellen. Die genauere Notation wird nachfolgend im Kapitel noch aufgezeigt, hier an dieser Stelle zunächst das Metamodell in Abbildung 6.14: Abhängigkeiten
Bei den Schnittstellen handelt es sich um eine vertragliche Vereinbarung darüber, welche Merkmale die einbindenden Klassen später enthalten sollten. Sie können grundsätzlich jeweils beliebig viele Eigenschaften und Operationen enthalten, wie Sie in Abbildung 6.15: Interfaces an der Kompositionsbeziehung ersehen können. Umgekehrt gehören die Eigenschaften und Operationen selbst immer keinem oder einem Interface.
Notation der KlassendiagrammeDie KlasseZunächst einmal gibt es in der UML 2.0 den Begriff eines benennbaren Elementes (NamedElement). Solch ein benennbares Element kann einen Namen haben und ist nach außen mit einer Sichtbarkeit verbunden. Eines dieser benennbaren Elemente in der UML 2.0 ist beispielsweise eine Klasse. Eine Klasse soll innerhalb eines Systems genau eine Aufgabe übernehmen. Somit übernimmt die Klasse eine Verantwortung für einen sachlogischen Aspekt des Gesamtsystems. Die Member dieser Klasse, also alle Attribute und Methoden, führen wiederum Aufgaben in einer Klasse aus, die in diesem Verantwortungsbereich liegen. Zunächst einmal wird eine Klasse durch ein dreigeteiltes Rechteck dargestellt. Der Klassenname steht in der Mitte des obersten Rechtecks, wie in Abbildung 6.16: Eine einfache Klasse mit fehlenden Attributen und Operationen
Die in dem Rechteck vorhandene Unterteilung kann (oder sollte sogar) im ersten Analyseschritt weggelassen werden, wenn es sich lediglich um die Beschreibung der Klasse handelt. In der Unterteilung kommt später in der ersten Unterteilung die Datentypen und in der zweiten die Methoden oder in der UML 2.0 die Operationen der. In dem ersten Schritt, der Beschreibung der Aufgabe der Klasse, ist es zunächst wichtig, einen aussagekräftigen Klassennamen zu benutzen. In der Analysephase, in der es darum geht, zunächst einmal alle notwendigen Klassen ausfindig zu machen, sollte die Klasse also tatsächlich wie in Abbildung 6.17: Eine einfache Klasse ganz ohne Attribute und Methoden dargestellt aussehen:
In diesem Beispiel handelt es sich um die Klasse Person. Erst in einem nächsten Schritt sollte genauer geklärt werden, welche Eigenschaften die Person haben kann, welche davon wichtig für die Modellierung des Systems sind und schließlich, was die Person alles tun und erledigen kann. Die Klasse wird also im nächsten Schritt unterteilt, und es werden die so genannten Eigenschaften und Operationen (Spezifikation: attributes, operations) in zwei weiteren rechteckförmigen Unterteilungen innerhalb der Klasse untergebracht, wie in Abbildung 6.18: Eine Klasse mit Attributen und Operationen dargestellt:
Die erste Unterteilung enthält die Attribute und die zweite Unterteilung enthält die Methodendeklarationen. Aus diesem Konstrukt erstellen einige Tools mehr oder weniger guten Quellcode in einer konkreten Programmiersprache. Nachfolgend werden wir meistens die letzte Notation der Klasse mit dieser Unterteilung nehmen, obwohl wir dabei nicht immer die Attribute und Operationen angeben. Wenn die Attribute und die Operationen nicht angegeben werden, ist natürlich auch die Notation wesentlich einfacher und oftmals in der Literatur gebräuchlicher. In der Spezifikation ist im Paket Kernel des Metamodells genau beschrieben. Dabei hat die UML 2.0, wie die meisten der objektorientierten Programmiersprachen, eine Oberklasse. In Java und in C# heißt diese Oberklasse Object. Hier in der UML-2.0-Spezifikation heißt sie Element. Soweit zur Theorie, denn das Element als oberste Basisklasse wird sicherlich niemand einsetzen, so viel ist sicher. Nur Klassen, die von Element erben, dürfen im Übrigen überhaupt instanziert werden, da die Klasse Element selbst abstractist. Dies bedeutet nämlich, dass von der Klasse geerbt werden darf, und von den Erblingen auch Instanzen gebildet werden können. Allerdings kann von einer abstrakten Klasse selbst keine Instanz gebildet werden. Somit vererbt die Klasse Element an alle Elemente der UML 2.0. Jedes Element hat somit alle Member von Element geerbt. Objekte und InstanzspezifikationWährend eine Klasse eine Schablone darstellt, ist das Objekt eine konkrete Ausprägung dieser Klasse. In der UML 2.0 unterliegt dieser Formalismus der Instanzspezifikation (Spezifikation: InstanceSpecification). Die genauen Werte für ein solches Objekt, also die genauen Attributwerte, nennen sich Slots. Ein konkretes Objekt sieht dann wie in Abbildung 6.19: Objekt aus.
Ein Objekt kann die Klasse enthalten, von der es konkret abgeleitet wird. Hier in unserem Beispiel handelt es sich um die Klasse Person. Der gesamte Aufbau ist dem der Klasse ähnlich, bis auf die Tatsache, dass der Name des Objekts unterstrichen wird. Zudem haben natürlich die Attribute konkrete Werte. Es ist im Übrigen nicht zwingend, für alle Attribute entsprechende Werte anzugeben. AttributeIn der UML 2.0 gibt es ein typisierbares Element (TypedElement). Es handelt sich dabei um ein benennbares Element (NamedElement), das neben einem Namen auch einen Typ haben kann. Attribute und Parameter sind beispielsweise solche Elemente. Eine Wertespezifikation (ValueSpecification) drückt einen Wert in einem Modell aus. Es handelt sich dabei letztendlich nur um mathematische Ausdrücke, die sich errechnen lassen. Ein Attribut muss innerhalb der Klasse eindeutig definiert sein. Die Attribute können mit folgender Syntax noch weiter spezifiziert werden: [Sichtbarkeit] [/] name [:Typ] [Multiplizität] [= Vorgabewert] [{Eigenschaftswert}]
In folgender Tabelle finden Sie eine Übersicht über die Merkmale der Syntax:
Tabelle 6.1: Allgemeine Syntax der Attributdeklaration Kommen wir zunächst auf die Sichtbarkeit zurück. Die nachfolgende Tabelle schlüsselt die einzelnen Sichtbarkeitsschlüsselwörter und deren Notation in der UML 2.0 auf:
Tabelle 4.2: Schlüsselwörter Kümmern wir uns nun um die so genannte Multiziplität. Die Multiziplität wird von eckigen Klammern umschlossen und legt jeweils die Unter- und Obergrenze der Ausprägungen eines Attributes fest. Und nicht nur in diesem Zusammenhang gibt es die Multiplizität (Spezifikation: MultiplicityElement). Wenn die Multiziplität die Möglichkeiten der Ausprägungen anzeigt, redet man von Kardinalität (Spezifikation: Cardinality), wenn man die Anzahl der Elemente in einer Menge benennen möchte. Hierbei handelt es sich also um die konkrete Anzahl der Ausprägungen. Nachfolgende Tabelle schlüsselt im Einzelnen die Möglichkeiten auf, die Multiziplität anzugeben.
Tabelle 4.3: Multiziplitäten Der Vorgabewert setzt quasi den Wert des Attributs per Default. Zunächst ist also der Wert des Attributes gesetzt, kann später allerdings zur Laufzeit verändert werden. Der Eigenschaftswert wird in geschweiften Klammern mitgeführt. Mit dem Eigenschaftswert können Sie die Eigenschaften des Attributs genauer festlegen. In folgender Tabelle sind die laut der Spezifikation möglichen Eigenschaftswerte aufgeführt:
Tabelle 4.4 Eigenschaftswerte: Hinter dem Element kann auch in geschweiften Klammern eine Zusicherung (Spezifikation: Constraint) notiert werden. Diese Zusicherung definiert mit Hilfe eines booleschen Ausdrucks, welcher Wertebereich zulässig ist. Zusicherungen werden grundsätzlich in geschweifte Klammern gefasst. Erlaubt ist die Zusicherung bei einem Element direkt hinter dem Namen oder innerhalb eines Kommentars, der direkt mit dem Element verbunden wird. Die Syntax für eine Zusicherung sieht wie folgt aus: `{` [<name> `:`] <boolean expression> `}`
MethodenEine Methode wird in der UML 2.0 auch als Operation (Spezifikation: operation) bezeichnet. Auch eine Methode muss innerhalb der Klasse eindeutig sein. Die Syntax sieht wie folgt aus: [Sichtbarkeit] Name (Parameterliste) : Rückgabetyp [{Eigenschaftswert}]
Die Parameterliste wird entsprechend der folgenden Syntax beschrieben: [Übergaberichtung] Name : Typ [`[`Multiplizität`]`] [= Vorgabewert]
[`{`Eigenschaftswert`}`]
Vieles, was zu den Attributen gesagt wurde, gilt auch uneingeschränkt über für die Methoden. Wir werden an entsprechender Stelle bei den Methoden darauf verweisen, was ähnlich ist.
Die so genannte Übergaberichtung des Parameters (Spezifikation: ParameterDirectionKind) kann folgende Schlüsselwörter enthalten:
Tabelle 4.5 ParameterDirectionKind: BeispielIm Folgenden werden wir Ihnen ein zusammenfassendes Beispiel mit je einem Attribut und einer Methode zeigen:
Wie Sie in dem Beispiel sehen, wird eine Eigenschaft seminartitelals String deklariert. Dieses Attribut hat die Multiplizität 1..*, was in diesem Kontext bedeutet, dass mindestens ein Seminartitel als Eigenschaft existieren muss, aber beliebig viele Seminartitel existieren können. Das Attribut ist als private deklariert und kann somit nur innerhalb der Klasse selbst genutzt werden. Von außen bleibt diese Eigenschaft unsichtbar. Der Standardwert (Spezifikation: default) ist UML 2.0, was hier nach dem Gleichheitszeichen folgt. Die Eigenschaft SeminarPreisist ebenfalls private. Sie enthält hier die Zusicherung (Spezifikation: Constraint), dass der Wert mindestens 670 Eurobetragen muss. Die Methode BucheSeminarist mit dem Schlüsselwort publicdefiniert. Die Methode hat einen Parameter namens SeminarTitel, der wiederum vom Typ Stringist. Durch das Schlüsselwort voidwird der Methode (Spezifikation: operation) mitgegeben, dass sie selbst keinen Rückgabewert hat. Sie gibt also keinen Wert zurück, sondern verarbeitet nur intern die Parameter, die ihr mitgegeben wurden. Die Methode SeminarPreishat einen Rückgabewert vom Typ Integer. Diese Methode gibt also einen Wert zurück, wenn sie aufgerufen wird. Dieser zurückgegebene Wert ist in jedem Fall vom Typ integer. Zudem ist diese Methode statisch, kann also ohne die Erzeugung eines Objektes genutzt und von außen gesehen werden. Gekennzeichnet wird dies in der UML 2.0 dadurch, dass die ganze Methode unterstrichen wird. Beachten Sie bitte, dass wir hier nur ein kleines Beispiel benutzen, um die Syntax von UML 2.0 zu erläutern. Die eigentliche Umsetzung erfolgt ja durch eine konkrete Programmiersprache wie C# oder Java. KommentareAn eine Klasse sowie an jedes andere Element aus der UML 2.0 können Kommentare (Spezifikation: Comment) angehangen werden. In der Spezifikation ist die Klasse Commentdirekt mit der Basisklasse Element assoziiert, was diese Tatsache direkt untermauert. Diese Kommentare sind wie folgt in der UML 2.0 standardmäßig zu notieren:
Wie schon im vorherigen Satz verraten, kann man den Kommentar an jedem UML-Modellelement notieren. Dies begründet sich darin, dass die Klasse Commentdirekt eine Assoziation mit der Basisklasse Elementbesitzt. SchnittstellenSchnittstellen (Spezifikation: interfaces) sind quasi ein Vertrag, den eine Klasse mit einer Schnittstelle schließt. Somit werden Methoden und set-und get-Methoden in der Schnittstelle definiert. Damit verpflichtet sich die Klasse, die Methode (Spezifikation: operation) der Schnittstelle zu definieren. Das Gleiche gilt hier auch für Attribute, auch wenn die meisten Programmiersprachen diese als Eigenschaftsmethoden bezeichnen. Die genaue Ausprogrammierung geschieht in der Klasse. Die Schnittstelle besteht daher nur aus den Methodenrümpfen und den Ein- und Ausgabeparametern. Ein Classifier (in den meisten Fällen wohl eine Klasse) kann beliebig viele Schnittstellen implementieren. Selbstverständlich kann die Klasse darüber hinaus weitere Methoden und Eigenschaften enthalten. Schnittstellen können untereinander jederzeit erweitert werden und voneinander erben. Hierbei wird die Generalisierungsbeziehung genutzt. Es gibt im Prinzip nunmehr zwei Möglichkeiten, eine Schnittstelle anzubringen. Somit gibt es eine bereitgestellte Schnittstelle und eine benötigte Schnittstelle. Im ersten Fall bietet eine Schnittstelle ein Modellelement an und kann von anderen Modellelementen genutzt werden. Im anderen Fall handelt es sich um eine Schnittstelle, die von einem anderen Modellelement eingefordert wird. Nun stellt sich natürlich die Frage, wie eine Schnittstelle implementiert wird. Dazu gibt es die Implementierungsbeziehung (Spezifikation: implementation), die eine spezielle Form der Realisierungsbeziehung darstellt. Zu der Realisierung, in der Programmierung zumeist Vererbung genannt, kommen wir im nächsten Abschnitt. Grundsätzlich wird das Interface genauso notiert wie die Klasse. Zusätzlich enthält dieser Classifier allerdings das Schlüsselwort <<interface>>.Das Schlüsselwort steht über dem Namen des Classifiers, ähnlich wie, aber nicht zu verwechseln wie mit einem Stereotyp. Stereotypen sind nicht Teil der Fundamental-Prüfung und die Logik dahinter ist nicht trivial und genauestens in der Spezifikation beschrieben.
Die Schnittstelle wird in der Klasse realisiert und steht somit in einer Realisierungsbeziehung (Spezifikation: realize). Nachfolgend wird die standardmäßige Notation verwendet.
Oftmals ist in der Literatur und auch in der Spezifikation an dem Pfeil (Spezifikation: Dependency-Pfeil) das Schlüsselwort <<realize>>angezeigt, wie hier auch geschehen. Es gibt noch eine weitere Möglichkeit, die Realisierung eines Interface zu notieren. In Abbildung 6.24: Realisierungsbeziehung mit der Steckernotation wird diese Möglichkeit aufgezeigt.
Die Notation aus der Abbildung nennt sich Steckernotation und ist die abgekürzte Notation, die gleichbedeutend mit der Abbildung ist. Allerdings zeigt diese Kurznotation nur die Namen der Schnittstelle, nicht die enthaltenen Operationen und Attribute. Für geforderte Schnittstellen gibt es auch zwei Möglichkeiten. Die Standardnotation ist analog zu der Realisierungsbeziehung (siehe Abbildung 6.25: Die Klasse fordert die Schnittstelle Person an.).
Auch hierfür gibt es eine Kurznotation:
Hierbei werden wiederum die enthaltenen Operationen und Attribute nicht angezeigt. Im Übrigen kann man auch beide Notationen mischen, dies stellt dann die Kombination von der Forderung und der Implementierung dar.
Generalisierung, VererbungAllgemein gesagt drückt die Generalisierung (Spezifikation: Generalization) eine Beziehung von einem allgemeinen Fall zu einem speziellen Fall der Klasse aus. Sie dient der Strukturierung von Aufgabengebieten einer Klasse. Die einzelnen Merkmale der Oberklasse werden durch die Generalisierung an die Elemente der Unterklasse weitergegeben. Eine Frage, die sich natürlich stellt, ist: Warum setzt man die Generalisierung überhaupt ein? Kurz gefasst ist sie eben ein geeigneter Weg, um Coderedundanzen zu vermeiden. Es ist dabei wichtig, eine Oberklasse zu finden, die grundlegende Eigenschaften hat und diese weiter vererbt. Der Erbling erbt somit diese Eigenschaften und kann selbst noch zusätzliches definieren. Wichtig zu bemerken ist, dass ein Objekt, das von einer Unterklasse instanziert wurde, jederzeit ein Objekt der Oberklasse ersetzen kann. Dies verbirgt sich in der Objektorientierung hinter dem Begriff des Substitutionsprinzips. Die Generalisierung dürfte den allermeisten Programmierern wohl durch den Begriff »Vererbung« bekannt sein. Die Generalisierung wird dargestellt durch einen Pfeil mit einer dreieckigen Spitze, der nicht ausgefüllt ist.
In der UML kann man das Überschreiben von Merkmalen (Spezifikation: ReDefinition) über die Metamodellklasse RedefinableElementsteuern. In einer Generalisierungsbeziehung dürfen alle Metamodellelemente überschreiben, sofern sie eine Unterklasse von RedefinableElementsind. Die Überschreibung erfolgt z.B. bei einem Attribut über den Eigenschaftswert {redefines}.
Mehrere Klassen können von einer Oberklasse erben. Daher ist der Begriff der Generalisierung auch sehr treffend, weil eben eine Oberklasse grundsätzliche Eigenschaften an eine Unterklasse weitergibt, die spezifischer wird. Abbildung 6.29: Eine Oberklasse mit mehreren Unterklassen zeigt die Notation einer Oberklasse, von der mehrere Unterklassen erben.
Oftmals möchte man ausdrücken, welchen Grund eine Generalisierung hat. Abbildung 6.30: Notation für eine Generalisierungsmenge (GeneralizationSet) zeigt eine Generalisierung mit der so genannten Generalisierungsmenge (Spezifikation: GeneralizationSet). Im Folgenden werden wir uns etwas genauer damit beschäftigen, was auch im Hinblick auf die Fundamental-Prüfung nicht schaden kann. Prüfungsthema ist dies allerdings erst in der Prüfung der Stufe Advanced.
Auf der rechten Seite in der Abbildung wird aufgezeigt, dass vom allgemeinen Fall »Personal« Klassen abgebildet werden, die den Mitarbeiterstatus genauer spezifizieren und zusätzliche Merkmale zu den Klassen hinzufügen. Beispielsweise wird ein Chef bestimmte Dinge tun und erledigen können, wozu ein Angestellter oder Praktikant nicht berechtigt ist. Allen Unterklassen ist aber gemeinsam, dass eine Personalnummer vergeben werden muss, egal ob es sich um einen Praktikanten, einen Angestellten oder einen Chef handelt. Die Generalisierungsmenge (Spezifikation: GeneralizationSet) benennt die Zusammensetzung der so genannten Generalisierungskanten. Die einzelnen Realisierungen bilden wiederum die so genannten Partitionen (Spezifikation: partitions). Weiterhin können die Generalisierungseigenschaften darunter in geschweiften Klammern angegeben werden, für die wiederum Schlüsselwörter angegeben werden müssen, die in nachfolgender Tabelle genauer erläutert werden.
Tabelle 4.6: Generalisierungsmenge Assoziationen (Associations)Die Assoziation (Spezifikation: associations) beschreibt grundsätzlich Beziehungen zwischen Klassen. Die Beziehung wird dabei genau beschrieben und benannt. Sie wird durch eine durchgezogene Linie zwischen Klassen dargestellt und kann einen Namen tragen. Inhaltlich kann die Beziehung dann durch natürliche Sprachelemente, aber auch durch grafische Notationen konkret beschrieben werden. Zwischen den Linien, die die Assoziationen darstellen, kann eine gestrichelte Linie gezogen werden. Mit dieser Notation können weitere Einschränkungen notiert werden. Nachfolgend zeigen wir auf, welche verschiedenen Ausprägungen auftreten können: Im einfachsten Fall besteht die Assoziation aus einer durchgezogenen Linie zwischen zwei Klassen. An der Linie kann die jeweilige Rolle der Klasse notiert werden, die die genaue Verwendung der Klasse bezeichnet. Die Personist also gemäß der nachfolgenden Abbildung in der Rolle des Fahrersim Kontext dieser Assoziation und das Autowird von dem Fahrergefahren. Bei dem Klassennamen ändert sich in diesem Zusammenhang nichts, die Rolle beschreibt lediglich die Klasse in dem Kontext der Assoziation, ähnlich wie ein Schauspieler eine Rolle spielt. Mit einer Rolle kann zudem eine Multiplizität angegeben werden. Wenn die Multiziplität nicht angegeben wird, ist 0..* voreingestellt. Die Assoziation enthält zudem einen Namen, dessen Bezeichnung mittig an die Linie angebracht wird. Wichtig hierbei ist zu erwähnen, dass auch zwei Assoziationslinien gezogen werden können, in der andere Rollen eingenommen werden. Zum Beispiel könnte die Persondas Auto verleihen, in dem Fall wäre die Personnicht der Fahrer,sondern der Verleiherdes Autos. Eine Rolle besagt nichts anderes als eine Eigenschaft (Property). Im folgenden Beispiel ist eine Personin die Rolle des Fahrersgelangt. Die Rollenangaben können hier wiederum ebenfalls eine Sichtbarkeit enthalten und im nachfolgenden Beispiel ist diese Sichtbarkeit in beiden Fällen public.Dies bedeutet, dass benachbarte Klassen Zugriff auf diese Operation des Objektes haben. In diesem Beispiel würde es Folgendes bedeuten: Ein Objekt mit dem Namen Fahrerder Klasse Personwird gebildet und kann dann dieses Auto fahren.
Vor den Namen kann man einen Schrägstrich (/)setzen, also (/Auto Fahren).Dies bedeutet, dass diese Assoziation von einer anderen Assoziation abgeleitet ist. Der Assoziation kann grundsätzlich auch mit einer so genannten Leserichtung versehen werden. Diese Leserichtung beschreibt die Beziehung zwischen den beiden Klassen näher.
Vom Prinzip besagt eine angegebene Leserichtung in etwa das, was auch eine Rollenangabe der Klasse innerhalb der Assoziation besagen würde. Allerdings ist die Rollenangabe weitaus aussagekräftiger als die Angabe der Leserichtung. Die Leserichtung sagt nur aus, dass die Ehefraugrundsätzlich die Personbegleitet, während die Personund die Ehefraufür sich genommen keine Rolle haben. Die Darstellungsweise in Abbildung 6.33: Angabe der Leserichtung mit einer Rolle ist nunmehr identisch mit der Angabe der Leserichtung.
Die Pfeilrichtung besagt, dass es sich um eine gerichtete Assoziation handelt. In diesem Fall heißt das, dass die Personin jedem Fall Kenntnis von der Ehefrauhat. Umgekehrt kann die Ehefrauvon der Person,die sie geheiratet hat, Kenntnis haben, muss es aber nicht zwingend, weil der Pfeil auf die andere Richtung nicht existiert. Dies ist natürlich rein logisch unrealistisch. Wenn also beide zwingend voneinander Kenntnis haben müssen, dann sähe die Notation wie in Abbildung 6.34: Beide Klassen haben zwingend Kenntnis voneinander aus:
Ein weiterer möglicher Fall ist, dass eine Klasse die andere Klasse kennen darf, oder sogar zwingend kennen muss, umgekehrt aber die gegenüberliegende Klasse die andere Klasse zwingend nicht kennen darf. Die Tatsache, dass eine Klasse die andere Klasse nicht kennen darf, wird mit einem Kreuz wie in Abbildung 6.35: Bankberater kennt zwingend den Kunden, aber nicht umgekehrt.deklariert:
Man spricht an dieser Stelle auch von unidirektionaler und bidirektionaler Assoziation. Bidirektionale Assoziation bedeutet, dass in beide Richtungen navigiert werden kann, während unidirektional bedeutet, dass nur eine Richtung erlaubt ist. Der Unterschied zwischen einer angegebenen Leserichtung und der gerichteten Assoziation ist, dass die Leserichtung lediglich zu einem besseren Verständnis für die Notation und den logischen Zusammenhang genutzt wird. Die gerichtete Assoziation hingegen bestimmt zwingend, ob ein Objekt auf ein anderes zugreifen darf. n-äre AssoziationEs gibt neben der gewöhnlichen Assoziation auch den Fall, dass mehr als zwei Assoziationsenden verfügbar sein müssen. Dies nennt man in der UML mehrgliedrige Assoziation. Dabei gibt es keine Möglichkeit, die n-äre Assoziation zu benennen, zumal aufgrund der Anordnung des Notationselementes kein Platz dafür vorhanden ist.
Natürlich können hier auch wiederum Multiplizitäten angegeben werden. Allerdings ist im Zusammenhang der n-ären Assoziation weder eine Aggregation noch eine Komposition erlaubt. Vererbung von AssoziationenAssoziationen können jederzeit generalisiert werden. Sie sind spezielle Classifier, somit steht ihnen quasi diese Möglichkeit offen. Es gibt einige formelle Regeln, an die es sich dabei zu halten gilt. Somit muss zum Beispiel die Anzahl der Assoziationsenden gleich bleiben und die beteiligten Klassen müssen gleich sein und entsprechend ebenfalls in einem Generalisierungszusammenhang stehen. Des Weiteren darf die Multiplizität der Assoziation bei dem Erbling nicht erweitert werden. Sie darf allerdings explizit eingeschränkt werden.
In Abbildung 6.37: Generalisierung von Assoziationen sehen Sie eine Anwendung der Assoziationsvererbung. In dem Fall erbt die untere Assoziation von der oberen und die unteren beiden Klassen erben alle Eigenschaften der oberen Assoziation. An dieser Stelle kann ein Generalisierungspfeil zusätzlich von der oberen Assoziationslinie zur unteren gezogen werden. AggregationBei der Aggregation handelt es sich um eine so genannte Teile-Ganzes-Beziehung. Sie ist im Grunde genommen ebenfalls eine Aggregation, die eine Beziehung darstellt, allerdings mit der Prämisse, dass sie nicht ganz gleichberechtigt ist. In der UML-2.0-Spezifikation der OMG übersetzt man die Aggregation mit »besteht aus«. Das Ende mit der leeren Raute bezeichnet die Klasse, die das Ganze darstellt. Das gegenüberliegende Assoziationsende ist ein Teil, das zum Ganzen gehört. Die Notation sieht wie in Abbildung aus.
In diesem Fall besteht ein Flugzeug aus mindestens einem Motor und maximal zwei Motoren, was die Multiziplität an den Aggregationsenden aussagt. Grundsätzlich könnte man auch eine Assoziationsbeziehung benutzen, um das Gleiche auszudrücken. Hier würde Flugzeug eine Rolle »besteht aus« zugewiesen und der Motoreben die Rolle »ist Teil von« zugewiesen bekommen. Am Assoziationsende des Ganzen ist eine Navigierbarkeit nicht möglich. Natürlich kann man die Notation mit einer Leserichtung versehen und Ähnliches. Die Lebensdauer des Ganzen überdauert die Lebensdauer der Teile und umgekehrt. Die Teile des Ganzen können sogar grundsätzlich auch für andere Klassen Teile sein. Die Multiziplitäten modellieren bei der Aggregation die Anzahl der Teile in einem Ganzen und in wie vielen Ganzen die einzelnen Teile vorhanden seien dürfen. KompositionDie Komposition stellt in diesem Zusammenhang eine weitaus strengere Form der Aggregation dar, bei der die Teile vom Ganzen sogar existenzabhängig sind. Es ist an dieser Stelle eine so genannte Inklusion der Teile zu einem Ganzen. In diesem Fall stellen Teile und das Ganze eine Einheit dar. Somit kann die Zerstörung dieser Einheit oder Teile daraus die Zerstörung des Ganzen zur Folge haben. Die Notation sieht wie in Abbildung 6.39: Komposition aus:
Die vorherige Abbildung besagt genau, dass ein Flugzeug kein oder ein Motor enthalten darf. Erst durch die Motoren wird das Flugzeug zu einem Ganzen. Hier wären also Segelflugzeuge sicherlich auch berücksichtig, denn Sie hätten dann kein Motor. Bei der Komposition ist die Auswahl der Multiziplitäten, die verwendet werden dürfen, beschränkt. Während ein Ganzes aus diversen Teilen bestehen kann, darf ein Teil nur zu genau einem Ganzen gehören. Es darf nicht zusätzlich noch zu einem anderen Ganzen gehören. Die Notation für die Multiziplität an der Raute kann weggelassen werden, da sie damit grundsätzlich 1 ist. Pakete und NamensräumeBei der Klasse haben Sie schon einmal das so genannte benennbare Element (NamedElement) kennen gelernt. In der UML 2.0 handelt es sich dabei um ein Element, das einen Namen und eine direkte Sichtbarkeit haben muss, die frei vorgebbar sind. In einem so genannten Namensraum (Namespace) können benennbare Elemente eingefügt werden. Wichtig ist, dass sie innerhalb des Namensraumes, der zum Beispiel eine große Ansammlung von Elementen genauer gliedert, eindeutig sind. Von außen kann man das Element mit dem qualifizierten Namen ansprechen. Da Namensräume ineinander verschachtelt sein können, können die qualifizierten Namen sehr ausschweifend werden und somit über mehrere Namensräume gehen. Die entsprechende Notation ist folgendermaßen: Namensraum1::Namensraum2::Klasse1 Zur besseren Strukturierung werden Klassen in so genannte Pakete gegliedert, die mit den Namensräumen zusammenhängen. Elemente, die in Paketen gegliedert werden dürfen sind die so genannten Paket-Elemente (PackageableElement). Dieses Konzept findet sich in vielen Programmiersprachen wieder, natürlich auch in Java und C#. Die UML 2.0 enthält eine Diagrammart, in der Pakete ausgedrückt werden. Damit kann man mehrere Classifier immer zu einem Paket zusammenfassen und erhält somit eine abstrakte Übersicht über verschiedene Funktionalitäten. Die einzelnen Pakete können beliebige Typen enthalten. Jedes Paket enthält einen Namensraum, in dem die Namen der Classifier eindeutig sein müssen. Jedes in einem Paket enthaltene Element kann über einen qualifizierten Namen (Paketname::Klassenname)angesprochen werden, innerhalb eines Paketes auch über einen unqualifizierten Namen. Dabei besteht der unqualifizierte Name nur aus dem Namen des Classifiers. Innerhalb eines Paketes kommt man in jedem Falle an einen anderen Classifier heran, außerhalb des Paketes kommt man ebenfalls unqualifiziert an die einzelnen Classifier heran, es hängt allerdings davon ab, wie die einzelnen Classifier zugreifbar sind. Hier gilt wiederum für privatedas Minuszeichen (-). Damit darf von außen nicht auf das Element zugegriffen werden. Für publicgilt dementsprechend das Pluszeichen (+), das besagt, dass von jeder Stelle des gesamten Modells auf dieses Element mit dem qualifizierten Namen zugegriffen werden darf. Die Pakete können wiederum selbst Pakete enthalten, wodurch sie hierarchisch gegliedert sind. Je nach Detaillierungsgrad kann man so die genaue Klassenanordnung einmal weglassen und das Gesamtsystem verdeutlichen und die Wiederverwendbarkeit übersichtlich darstellen. Ist ein Programmteil fertig, der auch für andere neu zu erstellende Programme nutzbar ist, kann man sich die vorhandene Programmlogik abstrakt verdeutlichen und im Zusammenhang mit der neu zu programmierenden Logik darstellen.
Abbildung 6.40: Paketdiagramm zeigt ein Paket mit zwei Klassen. Eine davon ist als privatedeklariert, die andere ist public. Auch bei einer Verschachtelung von Paketen kann die vorherige Notation übernommen werden. Hierbei können statt der Klassen jeweils die Pakete notiert werden. Ein Paket kann importiert werden, was sich durch einen gestrichelten Pfeil zwischen zwei Paketen darstellen lässt, an dem ein Stereotyp mit dem Schlüsselwort <<import>>angebracht ist. Es besagt, dass der Import publicist. Im Gegensatz dazu gibt es auch einen Import, der privateist. Dieser ist identisch, nur das Schlüsselwort lautet nunmehr <<access>>.
Vom Paket_2kann nunmehr die Klasse_4direkt angesprochen werden. Zudem kann von außen aus direkt ebenfalls über das Paket_2auf Klasse_4im Paket_4zugegriffen werden. Der Grund ist der <<import>>.Somit kann von Paket_2aus auf die Klasse_4,enthalten im Paket_4,direkt zugegriffen werden. Anders sieht es beim <<access>>aus. Hier kann zwar von Paket_2direkt auf die Klasse_1zugegriffen werden, die im Paket_1enthalten ist, allerdings geht das von außen über das Paket_2nicht. Somit kann aus dem Paket_3aus nicht auf die Klasse_1zugegriffen werden, weil Paket_2nur einen & <<access>>-Import, also einen privaten Import auf Paket_1hat. Es gibt darüber hinaus eine verkürzte Notation für den Import, das so genannte Paket-Merge. Hierbei wird der Zugriff mit <<merge>>notiert und beinhaltet eine Verschmelzung des Pakets, das auf diese Weise importiert wird. Vom Prinzip her spricht man an dieser Stelle von einer Generalisierung.
Abbildung 6.42: Darstellung einer Merge-Beziehung zeigt den <<merge>>-Import, der besagt, dass die Klasse 2im Paket 1 genauso angesprochen werden kann, als wäre diese direkt in Paket 1selbst vorhanden. An dieser Stelle spricht man beim <<merge>>-Import von einer Verschmelzung. Wichtig zu erwähnen ist, dass private Elemente nicht einbezogen werden. Formal kann man diese Verschmelzung gemäß der Spezifikation auch durch eine Generalisierung darstellen:
Wie Sie in Abbildung 6.43: Darstellung des Merge zwischen den beiden Paketen mit der Generalisierung erkennen, kann man innerhalb von Paket 1auch auf die Klasse 2direkt zugreifen, ohne den qualifizierten Namen Paket 2::Klasse 2zu benutzen. Es stellt sich nunmehr die Frage, was passiert, wenn in Paket 2 bereits eine eigene Klasse 2vorhanden gewesen wäre und man zusätzlich aus Paket 2eine andere Klasse 2mit dem gleichen Namen als Merge importiert. Die Antwort ist: Es entsteht eine komplett zusammengefasste neue Klasse 2mit allen Membern (Attributen und Operationen) der beiden Klassen. Zusammenfassend ist zu sagen, dass beim Import eines einzelnen Elements dieses Element per unqualifiziertem Namen angesprochen werden kann. Hierbei kann auch ein Aliasname verwendet werden. Ein Aliasname kann hingegen nicht verwendet werden, wenn ein komplettes Paket mit mehreren Elementen importiert wird. Ein Paket ist immer auch ein Namensraum, (Spezifikation: Namespace), das benennbare Elemente (NamedElement) beinhalten kann. In dem Namensraum selbst müssen diese Elemente eindeutig identifizierbar sein. Außerhalb dieses Namensraumes kann ein Element mit gleichen Namen existieren. Mit den so genannten qualifizierten Namen können diese Elemente direkt identifiziert werden.
Seminare
|