Modellierung mit UML - Teil 2: Konzepte

12.07.2005 von Bernd  Brügge und Allen  H. Dutoit
Die Modelliersprache Unified Modeling Language kommt bei objektorientierten Techniken zum Einsatz. Der zweite Teil unserer mehrteiligen Entwicklerserie beschäftigt sich mit den grundlegenden Konzepten der Modellierung.

Anwendungsbereiche umfassen meist Konzepte, mit denen Software-Entwickler nicht vertraut sind. Der Lösungsbereich (Benutzerschnittstellenwerkzeuge, drahtlose Kommunikation, Datenbanksysteme, Transaktionsüberwachungssysteme, tragbare Rechner, und so weiter) ist oft noch nicht ausgereift und bietet dem Entwickler viele sich widersprechende Implementierungstechnologien an. Folglich werden das System und das Entwicklungsprojekt komplex, weil sie viele Werkzeuge, Methoden und Leute umfassen.

Komplexität und Änderung stellen Herausforderungen dar, die es für einen Einzelnen oft unmöglich machen, das System und seine Entwicklung zu kontrollieren. Bei unsachgemäßer Kontrolle vereiteln Komplexität und Änderung die Lösung noch vor der Fertigstellung, selbst wenn das Ziel schon in Sicht ist. Zu viele Fehler in der Interpretation des Anwendungsbereichs machen die Lösung für den Benutzer nutzlos und erzwingen möglicherweise einen Rückzug vom Markt. Unzulängliche und inkompatible Implementierungstechnologien haben Unzuverlässigkeit und Verzögerungen zur Folge. Fehler bei der Behandlung von Änderungen erzeugen neue Fehler im System und machen das Produkt unbrauchbar.

Abhilfe schafft die Unified Modeling Language (UML), deren Ziel es ist, eine Standardnotation für alle objektorientierten Methoden zu bieten und dabei die am besten geeigneten Elemente aus bereits bestehenden Notationen auszuwählen und einzubinden. So verwendet UML zum Beispiel die bei Object-Oriented Software Engineering (OOSE) eingeführten Anwendungsfalldiagramme sowie viele Merkmale der Klassendiagramme aus der Object Modeling Technique (OMT).

Die mehrteilige Artikelserie ist dem Buch Objektorientierte Softwareentwicklung der Autoren Bernd Brügge und Allen H. Dutoit entnommen. Das Buch ist im Verlag Pearson Studium erschienen. Sie können das komplette, 752 Seiten starke Werk im tecCHANNEL Bookshop bestellen oder direkt als E-Book downloaden

Serie: Modellierung mit UML

Teil 1

Grundlagen

Modellierungskonzepte

In diesem Abschnitt erläutern wir die grundlegenden Konzepte der Modellierung. Als Erstes definieren wir die Ausdrücke System und Modell und erörtern den Zweck der Modellierung. Wir erklären ihre Beziehung zu Programmiersprachen und -begriffen wie Datentypen, Klassen, Instanzen und Objekten. Abschließend beschreiben wir, wie objektorientierte Modellierung bei der Abstraktion der Systemumgebung als Grundlage eines Systemmodells dient.

Systeme, Modelle und Sichten

Ein System ist ein gegliederter Satz kommunizierender Teile. Im Folgenden beschäftigen wir uns mit technisierten Systemen, die für einen bestimmten Zweck entworfen wurden, anders als die Systeme in der Natur wie beispielsweise das Planetensystem, dessen ultimativen Zweck wir nicht kennen. Ein Auto besteht aus vier Rädern, einem Fahrgestell, einer Fahrgastzelle sowie einem Motor und hat den Zweck, Leute zu befördern. Eine Uhr, bestehend aus einer Batterie, einem Schaltkreis, Rädchen und Zeigern, wurde zur Zeitmessung entworfen. Ein Lohnabrechnungssystem bestehend aus Großrechner, Druckern, Platten, Software und Lohnbuchhaltern wurde entworfen, um Gehaltsabrechnungen für die Beschäftigten einer Firma zu erstellen.

Teile eines Systems können manchmal wiederum als Systeme angesehen werden, die wir dann als Subsysteme bezeichnen. Der Motor eines Autos, bestehend aus Zylindern, Kolben, einem Einspritzmotor und vielen anderen Teilen, ist ein Subsystem eines Autos. Genauso sind der integrierte Schaltkreis einer Uhr und der Großrechner Subsysteme. Diese Zerlegung in Subsysteme kann rekursiv wiederum auf Subsysteme angewendet werden. Objekte stellen die Beendigung dieser Rekursion dar, wenn jedes Teil einfach genug geworden ist, um ohne weitere Zerlegung vollständig verstanden zu werden.

Viele Systeme bestehen aus zahlreichen Subsystemen, die auf komplizierte Weise miteinander verbunden sind. Oftmals sind die Verknüpfungen so komplex, dass ein einzelner Entwickler sie gar nicht mehr überschauen kann. Modellierung ist ein Mittel, um diese Komplexität in den Griff zu bekommen. Komplexe Systeme werden für gewöhnlich durch mehrere Modelle beschrieben, wovon jedes einen anderen Teilaspekt oder eine andere Genauigkeitsebene beschreibt. Modellierung bedeutet, eine Abstraktion eines Systems so zu konstruieren, dass die wichtigen Aspekte hervorgehoben werden und unerhebliche Einzelheiten unbeachtet bleiben. Was aber wichtig und was unerheblich ist, hängt von der jeweiligen Aufgabe ab.

Beispiel

Nehmen wir zum Beispiel an, wir wollten ein Flugzeug konstruieren. Selbst mit der Hilfe von Fachleuten können wir kein Flugzeug nur mit Hilfe einer groben Skizze bauen und dann hoffen, dass es auf seinem Jungfernflug einwandfrei funktioniert. Stattdessen bauen wir zuerst ein Maßstabsmodell des Flugzeugkörpers, um sein aerodynamisches Verhalten zu testen.

Bei diesem Maßstabsmodell müssen wir lediglich die Außenform des Flugzeugs darstellen. Einzelheiten wie Instrumententafel oder Motor können wir außer Acht lassen. Um Piloten für dieses neue Flugzeug auszubilden, bauen wir außerdem einen Flugsimulator. Der Flugsimulator muss die Anordnung und das Verhalten der Fluginstrumente genauestens wiedergeben. In diesem Fall können dafür aber Einzelheiten über das Äußere des Flugzeugs unbeachtet bleiben.

Sowohl der Flugsimulator als auch das Maßstabsmodell sind weit weniger komplex als das Flugzeug, das sie repräsentieren. Modellierung erlaubt es uns, mit Komplexität durch eine Teile-und-herrsche-Näherung umzugehen:

Für jede Art von Problem, das wir lösen wollen (beispielsweise Testen, aerodynamischen Verhaltens, Pilotentraining), bauen wir ein Modell, das ausschließlich auf die für dieses Problem wichtigen Bedürfnisse ausgerichtet ist. Verallgemeinert heißt das, dass Modellierung den Bau eines Modells zum Ziel hat, das einfach genug ist, um von einem Einzelnen verstanden zu werden. Das bedeutet aber auch, dass jedes Ganze höchstens 7 ± 2 Teile enthalten sollte.

Komplexität beim Modellieren

Modellieren hilft mit Komplexität umzugehen, weil wir dadurch einfache Modelle stufenweise so verfeinern, dass sie der Realität immer näher kommen. Wie in allen Ingenieursdisziplinen geht auch bei der Softwaretechnik für gewöhnlich das Modell dem endgültigen System voraus. Während der Analyse bauen wir zuerst ein für den Anwender verständliches Modell mit der allgemeinen Funktionalität, die das System erfüllen muss.

Dann verfeinern wir dieses Modell, indem wir mehr Einzelheiten über die Formen, die das System aufzeigen soll, sowie die Gestaltung der Benutzeroberfläche und die Antwort des Systems auf Ausnahmefälle hinzufügen. Der Satz aller Modelle, die während der Entwicklung erstellt werden, heißt Systemmodell. Verwendeten wir keine Modelle, sondern fingen gleich mit der Kodierung des Systems an, müssten wir schon alle Einzelheiten der Benutzeroberfläche spezifizieren, bevor uns der Kunde seine Anforderungen mitteilen könnte (und wir verlören auf diese Weise eine Menge Zeit und Mittel, falls der Kunde Änderungen verlangte).

Leider kann auch ein Modell so komplex werden, dass es kaum mehr zu verstehen ist. Wir können aber weiterhin die Teile-und-herrsche-Näherung verwenden, um auch ein so komplexes Modell in ein einfacheres zu verfeinern. Eine Sicht betrachtet einen Teilbereich eines Modells, um ihn verständlich zu machen (Bild 6). Zum Beispiel bilden alle zur Flugzeugkonstruktion nötigen Blaupausen ein Modell. Auszüge daraus, die erklären, wie die Treibstoffanlage funktioniert, bilden die Treibstoffanlagen-Sicht. Sichten können sich überschneiden: Die Sicht eines Flugzeugs, die die elektrische Schaltanlage beschreibt, umfasst natürlich auch die elektrische Schaltanlage für die Treibstoffanlage. Notationen sind grafische oder textuelle Regeln zur Darstellung von Sichten.

Datentypen, abstrakte Datentypen und Instanzen

Ein Datentyp ist eine Abstraktion im Kontext einer Programmiersprache. Ein Datentyp hat einen eindeutigen Namen, der ihn von anderen Datentypen unterscheidet. Dieser Name bezeichnet einen Satz von Werten, die diesem Datentyp angehören (dass heißt die Instanzen des Datentyps), und definiert die Struktur und die Operationen, die für alle Instanzen dieses Datentyps gültig sind. Datentypen werden in Typ-orientierten Sprachen verwendet und stellen sicher, dass nur gültige Operationen auf spezifische Instanzen angewendet werden.

Beispielsweise entspricht der Name int in Java den ganzen Zahlen zwischen –231 und 231–1. Gültige Operationen auf allen Instanzen dieses Typs sind die arithmetischen Operationen für ganze Zahlen (wie. Addition, Subtraktion, Multiplikation, Division) und all die Funktionen und Methoden, deren Parameter vom Typ int sind (beispilesweise mod). Der Java-Compiler meldet einen Fehler, falls eine Gleitpunktoperation auf eine Instanz vom Typint (wie etwa trunc oder floor) angewendet wird.

Ein abstrakter Datentyp ist ein durch eine implementierungsunabhängige Spezifikation definierter Datentyp. Ein abstrakter Datentyp ermöglicht es Entwicklern, einen Satz von Instanzen zu benutzen, ohne deren spezielle Implementierung zu kennen. Beispiele für abstrakte Datentypen sind Mengen, Sequenzen oder Abbildungen, die man mathematisch definieren kann. Ein System kann unterschiedliche Implementierungen des abstraktenDatentypsatzes unterstützen, wovon jede ein anderes Kriterium optimiert (beispielsweise Speicherverbrauch, Einfügezeit). Ein Entwickler, der zum Beispiel den abstrakten Datentyp Menge benutzt, braucht lediglich die Semantik von Mengenoperationen zu verstehen, aber nicht die interne Darstellung von Menge. Ein anderes Beispiel ist der abstrakte Datentyp Person mit den Operationen gibName(), gibSozialVersicherungsnummer() und gibAdresse()1.

Die Entscheidung, ob die Sozialversicherungsnummer einer Person als Zahl oder als Zeichenreihe gespeichert ist, ist aus der Sicht des restlichen Systems nicht wichtig. Solche Entscheidungen werden Implementierungsentscheidungen genannt.

Klassen, abstrakte Klassen und Objekte

Eine Klasse in der objektorientierten Modellierung und Programmierung ist eine Abstraktion. Wie bei den abstrakten Datentypen umfasst eine Klasse sowohl Struktur als auch Verhalten. Anders als bei abstrakten Datentypen können Klassen auch durch andere Klassen definiert werden. Angenommen wir haben eine Uhr, die auch als Taschenrechner benutzt werden kann. Dann kann die Klasse RechnerUhr als Verfeinerung der Klasse Uhr aufgefasst werden. Diese Art von Beziehung zwischen einer Basisklasse und einer verfeinerten Klasse wird Vererbung genannt. Die verallgemeinerte Klasse heißt Superklasse, die spezialisierte Klasse ist die Unterklasse. Bei einer Vererbungsbeziehung verfeinert die Unterklasse die Superklasse, indem neue Attribute und Operationen definiert werden. Im Bild definiert RechnerUhr eine Funktion zur Ausführung einfacher Arithmetik, die die Klasse Uhr nicht hat.

Wenn eine Vererbungsbeziehung ausschließlich dem Zweck dient, gemeinsame Attribute und Operationen zu modellieren, d.h. wenn eine Verallgemeinerung nie instantiiert wird, ist diese Klasse eine abstrakte Klasse. Abstrakte Klassen verkörpern oft verallgemeinerte Konzepte im Anwendungsbereich. Ihre Namen werden durch Kursivschrift dargestellt.

Zum Beispiel können in der Chemie Benzene als Klasse von Molekülen, die zur abstrakten Klasse OrganischeVerbindung gehören, aufgefasst werden. Man beachte, dass OrganischeVerbindung eine Verallgemeinerung ist und nicht einem Molekül entspricht, was besagt, dass OrganischeVerbindung keine Instanzen hat. In Java ist Collection eine abstrakte Klasse, für die es keine Instanzen gibt. Vielmehr sind alle Collection-Objekte Instanzen einer der Unterklassen von Collection, wie beispielsweise LinkedList, ArrayList oder HashMap. Man beachte, dass nicht alle Verallgemeinerungen abstrakte Klassen sind. Zum Beispiel ist die Uhr-Klasse im vorherigen Bild keine abstrakte Klasse, da sie selbst Instanzen haben kann. Wenn man Softwaresysteme modelliert, beziehen sich abstrakte Klassen manchmal auf keines der vorhandenen Konzepte des Anwendungsbereichs, sondern werden vielmehr eingeführt, um die Komplexität des Modells zu reduzieren oder seine Wiederverwendbarkeit zu vereinfachen.

Eine Klasse definiert die Operationen, die auf ihre Instanzen angewendet werden können. Operationen einer Superklasse können auch auf die Objekte der Unterklasse vererbt und angewendet werden. In Darstellung 8 ist die Operation setzeDatum(d), die das aktuelle Datum einer Uhr einstellt, auch für RechnerUhren verfügbar. Die in der RechnerUhr-Klasse definierte Operation setzeRechnerModus steht aber für die Uhr-Klasse nicht zur Verfügung.

Eine Klasse definiert die Attribute, die in all ihren Instanzen vorhanden sind. Ein Attribut ist eine bezeichnete Zelle in der Instanz, in der ein Wert gespeichert ist. Attribute haben eindeutige Namen innerhalb der Klasse und eines Typs. Die Klasse Uhr hat ein Zeit- und ein Datum-Attribut. Die Klasse RechnerUhr hat ein RechnerZustand-Attribut. Ein Objekt ist eine Instanz einer Klasse. Ein Objekt hat eine Identität und speichert Attributwerte. Jedes Objekt gehört zu genau einer Klasse. In UML wird eine Instanz durch ein Rechteck mit einem unterstrichenen Namen veranschaulicht. Diese Konvention wird in UML benutzt, um zwischen Instanzen und Klassen zu unterscheiden.

Im Bild 10 ist einfacheUhr1291 eine Instanz von Uhr und RechnerUhr1515 eine Instanz von Rechner-Uhr. Obwohl die Operationen von Uhr für RechnerUhr1515 verfügbar sind, ist RechnerUhr-1515 keine Instanz der Klasse Uhr. Die Attribute eines Objekts können bei einigen Programmiersprachen für andere Teile des Systems sichtbar sein. Zum Beispiel gestattet Java dem Programmierer zu spezifizieren, welche Attribute sichtbar sein sollen und welche nicht.

Ereignisklassen, Ereignisse und Nachrichten

Ereignisklassen sind Abstraktionen, die eine gewisse Art von Ereignis repräsentieren, auf die das System eine einfache Antwort hat. Ein Ereignis, eine Instanz einer Ereignisklasse, ist ein wesentlicher Bestandteil im System. Ein Ereignis kann beispielsweise ein Auslösereiz eines Akteurs (etwa „der UhrBenutzer drückt die linke Taste“), eine Unterbrechung (beispielsweise „nach 2 Minuten“) oder das Senden einer Nachricht zwischen zwei Objekten sein. Das Senden einer Nachricht ist der Mechanismus, mit dem das sendende Objekt die Ausführung einer Operation beim empfangenden Objekt fordert. Die Nachricht besteht aus einem Namen und einer Anzahl von Argumenten. Das empfangende Objekt vergleicht den Namen der Nachricht mit seinen Operationen und übergibt die Argumente an die passende Operation. Jedes Ergebnis wird an das sendende Objekt zurückgegeben.

In der nächsten Abbildung beispielsweise berechnet das einfacheUhr-Objekt die aktuelle Zeit aus der Greenwich-Zeit des Zeit-Objektes und der Zeitdifferenz des Zeitzone-Objektes, die es jeweils durch das Senden der Nachrichten gibZeit() und gibZeitDelta() bekommt.

Ereignisse und Nachrichten sind Instanzen: Sie stellen konkrete Begebenheiten im System dar. Ereignisklassen sind Abstraktionen, die Gruppen von Ereignissen beschreiben, auf die das System eine allgemeine Antwort hat. In der Praxis kann sich der Ausdruck „Ereignis“ auf Instanzen oder Klassen beziehen. Diese Mehrdeutigkeit wird durch Überprüfung des Kontexts, in dem der Ausdruck verwendet ist, beseitigt.

Ausblick

Im nächsten Teil unserer Serie über UML geben wir Ihnen einen tieferen Einblick in UML. Zunächst beschäftigen wir uns mit dem Objektorientieren Modellieren , Falsifikation und Erstellung eines Prototyps. Später gehen wir genauer auf die Anwendungsdiagramme und deren einzelnen Beziehungen ein.

Die Artikelserie ist dem Buch Objektorientierte Softwareentwicklung der Autoren Bernd Brügge und Allen H. Dutoit entnommen. Das Buch ist im Verlag Pearson Studium erschienen. Sie können das komplette, 752 Seiten starke Werk im tecCHANNEL Bookshop bestellen oder direkt als E-Book downloaden. (mja)

Serie: Modellierung mit UML

Teil 1

Grundlagen