Diplomarbeit - Goethe

Werbung
Johann Wolfgang Goethe – Universität
Frankfurt am Main
Diplomarbeit
Entwicklung
eines
Klassengraphen
von Carsten Rudolf Stocklöw
Fachbereich Biologie und Informatik
Prüfer:
Prof. Dr.- Ing. D. Krömker
Betreuer:
Dipl.-Inform. Tobias Breiner
vorgelegt am 30. September 2004
Inhaltsverzeichnis
Kapitel 1 Einleitung........................................................................................... 1
1.1
Software Engineering....................................................................................3
1.2
Refaktorisierung............................................................................................3
1.3
Sichten auf ein Software-System.................................................................. 4
1.4
Aufbau der Arbeit......................................................................................... 5
Kapitel 2 Graphische Darstellungsmöglichkeiten...........................................6
2.1
Objektorientierte Analyse- und Designmethoden.........................................6
2.2
UML............................................................................................................10
2.2.1
Geschichte.....................................................................................11
2.2.2
Überblick...................................................................................... 12
2.2.2.1 Die Four Layer Modeling Architecture........................ 12
2.2.2.2 Infrastructure und Superstructure................................. 14
2.2.2.3 Erweiterungsmöglichkeiten.......................................... 15
2.2.3
Diagramme und Views................................................................. 16
2.2.3.1 Klassen- und Objektdiagramm.................................... 19
2.2.3.2 Model Management – Paketdiagramm......................... 28
2.2.3.3 Weitere Diagramme......................................................30
Kapitel 3 Stand der Technik........................................................................... 38
3.1
Programm: ClassBuilder 2.4 Alpha 1.7...................................................... 38
3.2
Programm: Jumli 1.4...................................................................................40
3.3
Programm: Metamill v3.1 (build 556) ....................................................... 42
3.4
Programm: ObjectDomain R3 (build 292)................................................. 43
3.5
Programm: objectiF 4.7.............................................................................. 45
3.6
Programm: Rational Rose Enterprise Edition (2003.06.00.436.0)............. 47
3.7
Programm: WithClass 2000 Enterprise 6.0................................................ 49
3.8
Weitere Programme.................................................................................... 52
3.9
Fazit............................................................................................................ 54
Kapitel 4 Diskussion und Konzept..................................................................55
4.1
Die Programmiersprache C++.................................................................... 55
4.2
Die graphische Darstellung.........................................................................56
4.2.1
Datentypen, Zeiger und Referenzen..............................................57
4.2.2
Module und Komponenten........................................................... 58
4.2.3
Klassen..........................................................................................59
4.2.4
Methoden...................................................................................... 63
4.3
Synchronisation mit Sourcecode.................................................................64
4.4
Design......................................................................................................... 65
Kapitel 5 Implementierung............................................................................. 66
5.1
Parser, Präprozessor....................................................................................66
5.1.1
Alternative Darstellungen............................................................. 68
5.1.2
Präprozessor..................................................................................69
5.1.2.1 Symbolische Konstanten und Makros.......................... 69
5.1.2.2 Dateien einfügen........................................................... 71
5.1.2.3 Bedingte Kompilierung.................................................72
5.1.2.4 Weitere Direktiven........................................................72
5.1.2.5 Unterstützung durch das Programm............................. 73
5.1.3
Analyseverfahren.......................................................................... 75
5.2
Automatisches Layout.................................................................................79
5.2.1
Ästhetik.........................................................................................80
5.2.2
Layoutalgorithmen........................................................................81
5.3
Das Programm DiaClassma........................................................................ 84
5.3.1
Übersicht.......................................................................................84
5.3.2
Externe Komponenten.................................................................. 85
5.3.3
Graphische Benutzungsoberfläche............................................... 86
5.3.3.1 Explorer........................................................................ 87
5.3.3.2 Klassendiagramm......................................................... 88
5.3.4
Parser............................................................................................ 89
5.3.5
Datenverwaltung...........................................................................90
Kapitel 6 Bewertung........................................................................................ 92
Anhang A Glossar............................................................................................ 95
Anhang B Stand der Technik........................................................................100
Anhang C Inhalt der beigefügten CD-ROM............................................... 103
Anhang D Abbildungsverzeichnis................................................................ 104
Anhang E Literaturverzeichnis.................................................................... 106
Kapitel 1 Einleitung
Heutige Softwaresysteme gewinnen zunehmend an Komplexität. Manche
objektorientierte Programme bestehen aus mehreren hundert, wenn nicht sogar
tausend Klassen, die üblicherweise in Form einfacher Textdateien vorliegen.
Einen kompletten Programmcode aus hunderttausenden von Codezeilen zu
verstehen und damit zu arbeiten, während er kontinuierlich weiterentwickelt wird,
ist für einen Menschen nicht zu bewältigen. Deshalb entwickeln meist eine große
Anzahl von Leuten an einem Software-Projekt und sind nur für bestimmte
Bereiche zuständig. Aber auch hier muss man den Überblick behalten, sowohl für
das gesamte Software-System als auch für einzelne Teile, die im Zusammenspiel
aufeinander abgestimmt werden müssen.
Nun kann diese Problematik mit der Natur der Programmiersprachen
zusammenhängen, die als Sprachen per se linear orientiert sind. Es stellt sich die
Frage, ob graphische Darstellungen besser geeignet wären. Durch das Hinzufügen
einer zweiten oder gar einer dritten Dimension könnten Vererbungshierarchien
und vernetzte Zusammenhänge wie beispielsweise Funktionsaufrufe besser
visualisiert und durch das Ausblenden von Implementierungsdetails auf einen
Blick erfasst werden.
Im Laufe der Zeit haben sich einige Hilfen für Programmierer entwickelt:
•
Dokumentationserstellung
Anhand eines vorhandenen Sourcecodes wird eine Dokumentation erstellt,
beispielsweise im HTML-Format
•
Wizards
Wizards (engl. für Zauberer) sind kleine Helfer, die überall eingesetzt werden
können und üblicherweise durch die kurze Abfrage einiger Informationen eine
Aufgabe erfüllen. So kann es beispielsweise einen Wizard geben, der durch
Abfrage eines Klassennamens eine neue Klasse erstellt.
•
Editoren
Erweiterungen für einfache Texteditoren sind Syntax-Highlighting,
Klammerungsanzeige (zu einer Klammer wird die entsprechende andere
Klammer im Quelltext markiert), Vervollständigung von Wörtern, Sprung zur
Definition eines Elements, Ausblenden von Methoden-Code (selten benutzte
Methoden werden nur als Rumpf angezeigt), transparente Kommentare
1
Kapitel 1 Einleitung
•
(Graphische) Darstellung von Klassen, Methoden, Attributen und ihren
Beziehungen zueinander
•
Quellcodeverwaltung und Versionskontrollsysteme (CVS, RVS, SVN, ..)
•
Debugger
•
Automatische Erzeugung von Quellcode für bestimmte Aufgabenbereiche (z.B.
Parser- und Lexergeneratoren)
•
Dialogeditoren
•
Metriken
Metriken bilden Software auf einen Zahlenwert ab. Beispiel: Lines of Code
(LOC) ist die Anzahl der Programmzeilen
•
Profiler
•
...
Einige dieser Hilfen sind graphischer Natur und viele andere könnten graphisch
visualisiert werden. So könnte beispielsweise die Metrik LOC visualisiert werden
durch ein Balkendiagramm, in dem für jede Datei oder jede
Programmkomponente angegeben wird, aus wie vielen Codezeilen es besteht. Das
Programm Manhatten ([W3-Manhatt]) erzeugt aus Sourcecode eine virtuelle
dreidimensionale Stadt, in der jede Methode durch ein Hochhaus dargestellt wird.
Die Anzahl der Programmzeilen der Methode bestimmt die Höhe des Hauses und
die Menge an Kommentaren wird durch die grünliche Färbung der Rasenfläche
um die Häuser dargestellt.
Diesen Weg wollen wir hier jedoch nicht beschreiten. In der vorliegenden Arbeit
sollen nur Möglichkeiten untersucht werden, bei denen der Programmcode
graphisch dargestellt wird und bei denen eine Änderung in der graphischen
Darstellung auch zu einem veränderten Sourcecode führt. Dies kann man als
graphisches Programmieren bezeichnen.
Die Kernfrage, die in dieser Arbeit untersucht werden soll, ist, ob graphisch
orientierte Tools die Programmierung wesentlich beschleunigen können. Dabei
wird hauptsächlich auf eine – wenn möglich automatische – Visualisierung der
vernetzten Strukturen von Klassen und Methoden Wert gelegt.
2
1.1 Software Engineering
1.1
Software Engineering
Um Sourcecode graphisch darstellen zu können, muss er zuerst verarbeitet
werden. Aus mehreren simplen Textdateien werden so die Konstruktionselemente
gewonnen. Dies sind Klassen mit Attributen und Methoden sowie
Methodenkörper, die aus einfachen Anweisungen und Kontrollstrukturen
bestehen. Weiterhin werden die Beziehungen zwischen diesen Elementen gesucht.
Dieses Verfahren wird Reverse Engineering genannt. Das Gegenteil ist das
Forward Engineering, bei dem aus den Konstruktionselementen wieder ein
Sourcecode entsteht. Nimmt man beide Verfahren zusammen, so erhält man die
Möglichkeit, aus Sourcecode Konstruktionselemente zu erhalten, zu verändern
und wieder in Sourcecode zu überführen. Dies wird Roundtrip Engineering oder
Re-Engineering genannt. Werden keine Veränderungen vorgenommen, so sollte
man im Idealfall den ursprünglichen Sourcecode wieder erhalten.
1.2
Refaktorisierung
Ist das Reverse Engineering abgeschlossen, kann mit den Konstruktionselementen
gearbeitet werden. So ist auch Refaktorisierung möglich. Diese Bezeichnung
(engl.: Refactoring) wurde erstmals 1990 von William Opdyke und Ralph Johnson
([OpJo90]) benutzt und beschreibt das Umstrukturieren von vorhandenem
Sourcecode ohne dessen Funktionalität zu beeinträchtigen. Die Semantik bleibt
also erhalten, aber Lesbarkeit und Struktur des Sourcecodes wird verbessert.
Refaktorisierung beinhaltet u.a.:
•
Umbenennen
Vorhandenen Klassen, Attributen oder Methoden einen neuen Namen geben
•
Attribute kapseln
Es werden neue Methoden eingeführt, so dass ein Zugriff auf Attribute nur über
diese Methoden möglich ist
•
Methode extrahieren
Ein markierter Teil einer Methode wird extrahiert und als neue separate
Methode angelegt
•
Reihenfolge ändern
Das Ändern der Reihenfolge der Parameter einer Methode oder der
Reihenfolge der Methoden, in der sie im Sourcecode auftauchen
3
1.2 Refaktorisierung
•
Schnittstelle extrahieren
Anhand einer gegebenen Klasse CGeg wird eine neue Klasse CNeu erstellt, so
dass einige der Methoden von CGeg in CNeu als abstrakte Methoden definiert
werden. Zusätzlich wird eine Generalisierung eingeführt, so dass CNeu eine
Generalisierung von CGeg darstellt.
•
Subklasse extrahieren
Entsprechend der Extraktion einer Schnittstelle kann auch Funktionalität in
eine neue Subklasse ausgelagert werden.
•
Variable für einen Ausdruck anlegen
Wird in einem Codefragment mehrmals ein langer oder jedes mal neu
auszuwertender Ausdruck benutzt, so kann eine neue Variable eingeführt
werden, die am Anfang den Wert dieses Ausdrucks annimmt. Jedes
Vorkommen des Ausdrucks im Codefragment wird durch die Variable ersetzt.
1.3
Sichten auf ein Software-System
Ein Software-System kann aus verschiedenen Perspektiven und für
unterschiedliche Aufgabenbereiche betrachtet werden. Dabei werden individuelle
Aspekte des Systems hervorgehoben und bestimmte Details oder Programmteile
unterdrückt. Diese Ansichten können sein:
•
Überblick über das Gesamtsystem
Hier ist kein Wissen über einzelne Bereiche oder Implementierungen nötig,
sondern es werden einzelne Module und Komponenten betrachtet.
•
Zusammenspiel einzelner Komponenten
Mehrere Gruppen von Programmierern arbeiten an einzelnen Modulen bzw.
Bereichen; das Zusammenspiel dieser Komponenten muss funktionieren. Hier
sind Schnittstellen von besonderem Interesse.
•
Neuer Programmierer oder alter Code
Oft ist nur minimale oder keine Dokumentation vorhanden, um sich mit einem
neuen Programm oder einem Codefragment, das lange nicht mehr
weiterentwickelt wurde, vertraut zu machen. Automatisch generierte
Informationen mit hohen Detailgrad über vorhandenen Sourcecode ist von
Nutzen.
•
Programmierung
Die Unterstützung
der
Programmierung
einzelner
Methoden
durch
4
1.3 Sichten auf ein Software-System
Visualisierung von Programmcode erleichtert den
verschachtelte Schleifen und bedingte Ausführungen.
1.4
Überblick
über
Aufbau der Arbeit
Im zweiten Kapitel wird der momentane Standard zur Darstellung von SoftwareSystemen vorgestellt: die Unified Modelling Language UML. Es werden die
geschichtlichen Hintergründe dieser Notation sowie ein Überblick über die
verschiedenen Diagramme gegeben.
Das dritte Kapitel beschäftigt sich mit dem Stand der Technik. Dazu werden
mehrere konkrete Programme auf ihre graphische Darstellung und die
Möglichkeiten zur Modifikation von Sourcecode getestet.
Im vierten Kapitel wird ein eigenes Konzept erarbeitet. Dabei werden die
Schwachstellen der getesteten Programme berücksichtigt und auf die spezifische
Aufgabenstellung angepasst.
Im fünften Kapitel werden Möglichkeiten zur Implementierung und der konkreten
Umsetzung beschrieben
Den Abschluss bildet das sechste Kapitel mit einer Bewertung des Konzepts
sowie der Implementierung.
5
Kapitel 2 Graphische Darstellungsmöglichkeiten
Möglichkeiten zur graphischen Darstellung von Programmcode gibt es schon sehr
lange. Dazu zählen einzelne Diagramme genau wie strukturierte Methoden, die
den kompletten Entwicklungsprozess beschreiben und einzelne Phasen dieses
Prozesses in Form von Diagrammen graphisch darstellen. Nach dem Entstehen der
ersten objektorientierten Programmiersprache (Simula-67 aus dem Jahr 1967 gilt
als erste objektorientierte Programmiersprache) wurden auch Methoden und
Notationen für objektorientierte Systeme entwickelt.
2.1
Objektorientierte Analyse- und Designmethoden
Seit den späten 80er Jahren macht man sich Gedanken darüber, wie man bei der
Entwicklung komplexer objektorientierter Softwaresysteme vorgehen soll. Über
die Jahre hinweg wurden so mehr als 50 Methoden beschrieben (weshalb diese
Zeit oft als Methodenkrieg bezeichnet wird). Dabei wird üblicherweise der
komplette Entwicklungsprozess beschrieben und in einzelne Phasen unterteilt. Es
werden Tätigkeiten definiert und Zeitangaben für einzelne Phasen gegeben. Es
werden hauptsächlich zwei Schritte unterschieden: Analyse und Design. Während
der Analyse wird untersucht, was erstellt werden soll, und beim Design wird
entschieden, wie dies umzusetzen ist. Zur Dokumentation und Visualisierung der
einzelnen Phasen und des Programmcodes werden verschiedene textuelle und
graphische Darstellungsformen (Notationen) vorgegeben.
Die Methoden sind nicht alle unabhängig voneinander entstanden. Es gab immer
wieder Beeinflussungen von anderen Methoden, die nicht notwendigerweise
objektorientiert sein mussten, und es gab Bemühungen, die Vorteile der
verschiedenen Methoden zu vereinigen. Ein solcher Versuch ist mit UML
gelungen und manche Methoden benutzen UML als Notation (z.B. UP (RUP),
Team Fusion, Catalysis, ...), weshalb im Folgenden nur eine Auflistung einiger
Methoden erfolgen soll.
6
2.1 Objektorientierte Analyse- und Designmethoden
Abk.
Name und Homepage
Autor
Jahr
Information
BON
Business Object Notation
(http://www.bon-method.com)
Nerson,
Walden
1992
[WaNe95]
Booch
Booch (Object-Oriented Analysis Booch
(OOAD) and Design) (zwei Versionen:
Booch'91 und Booch'93)
1991+ [Booch91]
1993 [Booch94]
Catalysis Catalysis
(http://www.catalysis.org)
D'Souza,
Wills
1999
[HeEd94a]
CRC
Class-ResponsibilityCollaboration
Wilkinson
1989
[BeCu89]
[WiNa95]
EROOS
Entity-Relationship Based
Object-Oriented Specifications
Lewi,
1990
Steegmans,
Van Baelen
[Lewi90]
Firesmith
[Fire93]
Firesmith Firesmith
1993
Fusion
Fusion (später Team Fusion unter Coleman
Benutzung von UML)
1994+ [Cole94]
1997
HOOD
Hierarchical Object-Oriented
Design
Robinson
1992
HOORA Hierarchical Object Oriented
Requirements Analysis
(http://www.hoora.org)
European
Space
Agency
(ESA)
1995
[Robi92]
MOSES
Methodology for Object-Oriented Henderson- 1994
Software Engineering of Systems Sellers,
Edwards
[HeEd94b]
OBA
Object Behaviour Analysis
Rubin,
Goldberg
1992
[RuGo92]
OMT
Object Modelling Technique
(zwei Versionen: OMT und
OMT94)
Rumbaugh
u.a.
1991
[Rumb91]
OOA /
OOD
Object-Oriented Analysis /
Object-Oriented Design
Coad,
Yourdon
1991
[CoYo91a]
[CoYo91b]
OOCM
Object-Oriented Conceptual
Modeling
Dillon, Tan 1993
[DiTa93]
OODLE
Object-Oriented Design
Language
Shlaer,
Mellor
[ShMe92]
1992
7
2.1 Objektorientierte Analyse- und Designmethoden
Abk.
Name und Homepage
Autor
Jahr
Information
OOSA
Object-Oriented System Analysis Shlaer,
Mellor
1988
[ShMe88]
OOSC
Object-Oriented Software
Construction
Meyer
1994
[Meyer97]
OOSD
Object-Oriented System
Development
Champeaux, 1993
Lea, Faure
[ChLeFa93]
OOSE
Object Oriented Software
Engineering(zwei Versionen:
OOSE und OOSE'94)
Jacobson
[Jaco92]
OPEN
(OML)
Object-Oriented Process,
Environment and Notation
(OPEN Modelling Language)
(http://www.open.org.au)
Henderson- 1996
Sellers,
Graham u.a.
[Hend96]
RDD
Responsibility Driven Design
(DOOS) (Designing Object-Oriented
Software)
Wirfs-Brock 1990
[Wirf90]
ROOM
Real-Time Object-Oriented
Modeling
Selic,
Gullekson,
Ward
1994
[SeGuWa94]
RUP
Rational Unified Process (Unified Jacobson,
Process angepasst für Rational
Booch,
Software)
Rumbaugh
1998
[Kruch98]
SOMA
Semantic Object Modelling
Approach
Graham
1995
[Graham95]
Sully
Sully
Sully
1993
[Sully93]
Syntropy Syntropy
Cook,
Daniels
1994
[Cook94]
UP
Unified Process
Jacobson,
Booch,
Rumbaugh
1998
[Kruch98]
XP
Extreme Programming
Beck
(www.extremeprogramming.org)
1999
[Beck99]
1992
8
2.1 Objektorientierte Analyse- und Designmethoden
OOSA
CRC
RDD
EROOS
1990
OOA / OOD
Booch '91
OMT
OBA
HOOD
OOSE
OOCM
Sully
Booch '93
Fusion
BON
OOSD
OODLE
Firesmith
OOSE 94
OMT 94
Syntropy
ROOM
MOSES
OOSC
Unified Method 0.8
SOMA
HOORA
1995
UML 0.9 & 0.91
OPEN / OML
UML 1.0
Team Fusion
UML 1.1
UP (RUP)
UML 1.2
UML 1.3
Catalysis
XP
2000
UML 1.4
UML 1.5
UML 2.0
2004
Abbildung 1: Objektorientierte Methoden und Notationen im historischen Kontext
9
2.2 UML
2.2
UML
Die Unified Modeling Language UML ist keine Methode, sondern eine graphische
Notation zur Visualisierung, Dokumentation, Konstruktion und Spezifikation von
Software-Systemen. Sie wurde standardisiert von der
Object Management Group OMG ([W3-OMG]).
Dieses 1989 von elf Gesellschaften gegründete Nonfor-Profit Konsortium besteht mittlerweile aus etwa
800 Mitgliedern mit dem Ziel, die Model Driven
Architecture MDA zu etablieren und weiter zu
entwickeln.
Mit OMG's MDA wird Software plattformneutral und auf einer hohen abstrakten
Ebene modelliert, um dann mit Hilfe von Generatoren Quellcode erzeugen zu
können. Dadurch soll die Softwarequalität und Handhabbarkeit verbessert, die
Wartbarkeit vereinfacht und die Entwicklungsgeschwindigkeit gesteigert werden.
Die dazu benutzten Standards sind u.a. UML, Meta-Object Facility (MOF) und
XML Meta-Data Interchange (XMI).
UML wird unterstützt von namhaften Firmen wie HP, IBM, Microsoft und Oracle.
Sie wird seit dem Beginn im Oktober 1994 kontinuierlich weiterentwickelt und ist
mittlerweile ein weltweit anerkannter Standard.
Detaillierte Informationen und Spezifikationen können unter [W3-UML]
nachgesehen werden. Eine Übersicht über die UML-Notation und somit eine gute
Referenz wird von [W3-OOSE] zum Download angeboten (dort ist ebenfalls ein
Becher mit aufgedruckter UML-Referenz bestellbar; eine Übersetzung ins
klingonische ist in Vorbereitung). Eine ausführliche Einführung in das
Metamodell, Super- und Infrastructure kann in [SofDevUML2] nachgelesen
werden. Einige Beispiele sind [UML2Toolkit] und [Jeckle04] entnommen. Eine
Liste mit deutschen Übersetzungen kann unter [W3-JECKLE] eingesehen werden.
10
2.2 UML
2.2.1
Geschichte
UML wurde maßgeblich von den drei Autoren James Rumbaugh, Grady Booch
und Ivar Jacobson entwickelt, deren Methoden sich großer Beliebtheit erfreuten.
Als im Oktober 1994 Rumbaugh
zu Rational Software Corporation
wechselte, begann er mit dem
dort beschäftigten Booch an einer
Vereinheitlichung
ihrer
Methoden OMT und Booch zu
arbeiten. Ein erster Entwurf
wurde unter dem Titel Unified Abbildung 2: Die drei Amigos: Grady Booch, James
Rumbaugh, Ivar Jacobsen (v.li.n.re.)
Method 0.8 im Oktober 1995 auf
der OOPSLA veröffentlicht. Etwa zum selben Zeitpunkt wechselte Ivar Jacobsen
zu Rational und seine Methode OOSE wurde integriert. Dieses Trio – bekannt
unter dem Namen „Die drei Amigos“ – veröffentlichten das Ergebnis ihrer Arbeit
unter dem inzwischen geänderten Titel Unified Modeling Language in den
Versionen 0.9 im Juni 1996 und 0.9.1 im Oktober 1996.
Im Januar 1997 wurde die UML Version 1.0 der OMG zur Standardisierung
eingereicht als Antwort auf ihren RFP (Request for Proposal) für eine
standardisierte Modellierungssprache. Nach der Kombination mit anderen
Vorlagen entstand die Version 1.1, die im Juli der OMG offeriert, im September
von OMG Analysis and Design Task Force (ADTF) und OMG Architecture
Board akzeptiert und schließlich im November 1997 als Standard anerkannt
wurde.
In den folgenden Jahren erschienen weitere Versionen mit kleinen Änderungen.
Aktuelle Version ist UML 1.5. UML 2.0 mit größeren Änderungen ist für Ende
2004 vorgesehen. Da sich diese Version im letzten Schritt des
Standardisierungsprozesses befindet, sind keine technischen Änderungen mehr zu
erwarten, weshalb die Änderungen bereits in einigen Programmen umgesetzt
wurden und auch hier betrachtet werden.
11
2.2 UML
2.2.2
2.2.2.1
Überblick
Die Four Layer Modeling Architecture
Eine Klasse beschreibt eine Menge gleicher Objekte. Die Klasse definiert dabei
das allgemeine Aussehen, d.h. die Eigenschaften in Form der Attribute und das
Verhalten in Form der Methoden. Eine Instanz einer Klasse ist das Objekt, das
spezifische Werte hat. So kann beispielsweise die Klasse Mensch definiert werden
mit einer Zahl als Attribut, die das Alter angibt, und einer Methode, die aus
diesem Alter und dem aktuellen Datum die Volljährigkeit ermittelt. Ein konkretes
Objekt – also eine Instanz dieser Klasse – ist dann z.B. Paul mit einem Alter von
25 Jahren.
Somit ist die Klasse ein Modell von mehreren unterschiedlichen Objekten. Die
Klasse gibt eindeutig vor, wie die Objekte aussehen und was sie können. Benutzt
man dieses Konzept und abstrahiert weiter, dann erhält man ein Metamodell.
Dieses Metamodell beschreibt eindeutig, wie eine Klasse auszusehen hat. Es
definiert also, dass eine Klasse Attribute und Methoden hat und dass sie
Superklassen/Subklassen durch Vererbung haben kann. Ein Element dieses
Metamodells ist Classifier und eine Instanz dieses Elements ist beispielsweise
eine Klasse. UML ist ein solches Metamodell. „Meta“ bedeutet in diesem
Zusammenhang, das es sich um ein Modell handelt, mit dem man Modelle
beschreiben kann. Tatsächlich kann UML mit UML erklärt werden.
Nun ist UML nicht nur für eine bestimmte oder eine kleine Gruppe von
Programmiersprachen entworfen worden. Es muss also möglich sein, das
Metamodell anzupassen. Die Fähigkeit der Mehrfachvererbung beispielsweise ist
nicht in allen Sprachen gegeben. In C++ können Klassen von beliebig vielen
anderen Klassen erben, in Java ist nur eine Einfachvererbung möglich. Um
festzulegen, wie das Metamodell geändert werden darf, wurde eine weitere
Schicht eingeführt: das Meta-Metamodell. Auch hier kommt dasselbe Konzept
zum Einsatz. Das Meta-Metamodell beschreibt das Metamodell. Instanzen der
Elemente aus dem Meta-Metamodell beschreiben ein Metamodell.
Natürlich könnte man an dieser Stelle noch weiter abstrahieren und würde ein
Meta-Meta-Metamodell usw. erhalten. Nach langen Diskussionen hat man jedoch
eingesehen, dass weitere Metaschichten keinen zusätzlichen Abstraktionsgewinn
bringen. Somit haben wir insgesamt vier Schichten oder Ebenen. Dies wird VierSchichten-Modellierungsarchitektur oder Four Layer Modeling Architecture
12
2.2 UML
genannt. Die einzelnen Schichten werden durchnummeriert mit M0 bis M3, wobei
das Meta-Metamodell die Schicht M3 darstellt.
Zur Beschreibung des Meta-Metamodells kann ein weiterer Standard der OMG
benutzt werden. Die Meta-Objects Facility MOF, die auch zur Definition anderer
Technologien wie die Komponenten- und Schnittstellenbeschreibungssprache
CORBA IDL (Common Object Request Broker Interface Definition Language)
eingesetzt wird. MOF definiert das MOF-Modell, das ein Meta-Metamodell
darstellt. Instanzen der Elemente des MOF-Modells sind Elemente eines
Metamodells. Um einen standardisierten Austausch von Daten zwischen
unterschiedlichen Programmen zu ermöglichen wurde XMI (XML Metadata
Interchange) ins Leben gerufen. XMI beschreibt, wie Instanzen der Elemente des
MOF-Modells auf XML Definitionen abgebildet werden können. Somit ist ein
Austausch von UML-Modellen möglich. Auch XMI ist ein Standard der OMG.
Meta-Metamodell (M3 = MOF)
Classifier
«instanceOf»
Metamodell (M2 = UML)
Attribut
Classifier
«instanceOf»
Modell (M1)
«instanceOf»
«instanceOf»
Mensch
+ Alter : Integer
«instanceOf»
Objekte (M0)
Paul : Mensch
Alter = 25
Abbildung 3: Four Layer Modeling Architecture von UML
13
2.2 UML
2.2.2.2
Infrastructure und Superstructure
Zwei der Vier Schichten der Four Layer Modeling Architecture bedürfen der
Standardisierung und finden sich tatsächlich auch in zwei unterschiedlichen
Dokumenten wieder. Die UML Infrastructure erklärt das Meta-Metamodell und
die UML Superstructure das Metamodell.
Die UML Infrastructure definiert
Modellelementen und Unterpaketen:
vier
Pakete
mit
unterschiedlichen
1. PrimitiveTypes: vordefinierte Datentypen Integer, Boolean, String und
UnlimitedNatural (natürliche Zahlen und der Asterisk ('*') für
unendlich)
2. Basic:
Klasse, Attribut, Operation, Paket
3. Abstractions:
abstrakte Metaklassen, die zur weiteren Spezialisierung
gedacht sind und voraussichtlich in vielen Metamodellen
benutzt werden: Generalisierung, Instanz, Multiplizität,
Sichtbarkeit, Einschränkungen (Constraints) u.a.
4. Constructs:
Assoziation
Diese vier Pakete werden zusammengefasst in einem Paket namens Core und
bilden somit die Grundlage für die UML Infrastructure.
Core
PrimitiveTypes
Abstractions
Basic
Constructs
Abbildung 4: Core Pakete der UML Infrastructure
14
2.2 UML
Die UML Superstructure beschreibt das Metamodell, also UML selbst. Dabei
werden die Elemente aus der Infrastructure übernommen, d.h. alle Elemente der
Superstructure sind Instanzen der Infrastructure. Weiterhin werden alle Elemente
der Superstructure importiert. Sie stehen also auch im Metamodell zur Verfügung.
Das zentrale Paket hier wird als Kernel bezeichnet.
2.2.2.3
Erweiterungsmöglichkeiten
UML bietet mehrere Möglichkeiten zur Erweiterung und auch zur Einschränkung
unterschiedlicher Elemente von UML. Dies sind Constraints (Einschränkungen
oder Zusicherungen), Tagged Values (Eigenschaftswerte) und Stereotypen.
Benutzt man eine oder mehrere dieser Erweiterungen, so erhält man ein
sogenanntes Profile, das auch mittels XMI weitergegeben werden kann.
Stereotypen sind Namen, die in Guillemets eingefasst sind ( «Stereotyp-Name» )
und können auf unterschiedliche Modellelemente angewendet werden. Viele
vordefinierte Stereotypen sind bereits in UML enthalten, z.B. «class», das eine
Klasse beschreibt. Zusätzlich kann ein Icon, also eine graphische Repräsentation
angegeben werden. Es können Stereotyp oder Icon oder beides angezeigt werden.
Ein Beispiel für vordefinierte Stereotypen und Icons
sind Control, Boundary und Entity. Sie basieren auf
dem Model-View-Controller Modell (MVC-Modell),
Abbildung 5: Icons für die
mit dem die in Anwendungen oft benutzte Trennung
Stereotypen Control, Entity,
Boundary
in Eingabe, Verarbeitung und Ausgabe auf GUIbasierte Systeme übertragen wird. Die Eingabe entspricht dabei der MVCKomponente Controller und dem UML-Stereotyp «control». Die Verarbeitung
wird von der MVC-Komponente Model und dem UML-Stereotyp «entity»
übernommen, während für die Ausgabe die MVC-Komponente View und dem
UML-Stereotyp «boundary» verantwortlich ist und üblicherweise durch Fenster,
Dialoge oder Kommunikationsklassen wie TCP/IP realisiert werden.
Tagged Values bestehen aus einem Namen und einem zugehörigen Wert. Dadurch
können die unterschiedlichsten Informationen untergebracht werden, wie den
Status der Entwicklung, Informationen über Autor und Zweck. Sie werden in
geschweifte Klammern gefasst, beispielsweise {Autor = „Paul“} oder {Status =
„muss noch gemacht werden“, Fertigstellung = „2004“}. Diese Informationen
tauchen üblicherweise nicht im fertigen Produkt auf, sondern werden nur im
Modell angezeigt. Ist der zugehörige Wert des Tagged values vom Typ Boolean
und true, so muss der Wert nicht angezeigt werden (Beispiel: {abstract}).
15
2.2 UML
Constraints sind Einschränkung der Benutzung oder
Seniorengruppe
Bedeutung von Elementen. Dies soll an einem Beispiel
verdeutlicht werden: Seien zwei Klassen namens
{Person.alter > 60}
„Person“ (mit Attribut „Alter“) und „Seniorengruppe“
Person
gegeben sowie eine Beziehung zwischen diesen
Klassen. Will man sicherstellen, dass nur Personen ab
alter : Integer
60 Jahren einer Seniorengruppe zugewiesen werden
Abbildung 6: Beispiel für
können, dann kann das realisiert werden mit einem
Constraints
Constraint, das der Beziehung zugewiesen wird. Dieses
hat die Form {person.alter > 60}. Üblicherweise werden Constraints in einer
speziell für diesen Zweck ausgelegten Sprache – der Object Constaint Language
OCL – verfasst. Aber auch andere Sprachen sind möglich.
2.2.3
Diagramme und Views
UML 2.0 definiert mehrere Diagramme, die in die zwei Bereiche Struktur und
Verhalten eingeteilt werden können:
Strukturdiagramme (Structural Diagrams)
•
Klassendiagramm (Class Diagram)
Zeigt Klassen mit ihren Attributen und Operationen sowie die
Beziehungen untereinander.
•
Objektdiagramm (Object Diagram) oder Instanzdiagramm (Instance
Diagram)
Ähnlich dem Klassendiagramm werden hier Objekte mit spezifischen
Werten dargestellt. Dies ist nur eine Momentaufnahme des Systems.
•
Komponentendiagramm (Component Diagram)
Mit dem Komponentendiagramm werden Komponenten und ihre
Beziehungen untereinander angezeigt. Komponenten sind hierbei
modulare und austauschbare Teile des Systems mit eindeutig definierter
Schnittstelle.
•
Verteilungsdiagramm (Deployment Diagram)
Zeigt auf, wie das System auf unterschiedliche Computer verteilt ist und
wie diese miteinander kommunizieren.
•
Kompositionsstrukturdiagramm (Composite Structure Diagram)
Beschreibt die interne Struktur einer Klasse oder Komponente.
16
2.2 UML
•
Paketdiagramm (Package Diagram)
In einem Paketdiagramm können verschiedene Modellelemente gruppiert
werden. Dabei wird ein eindeutiger Namensraum festgelegt.
Verhaltensdiagramme (Behavior Diagrams)
•
Anwendungsfalldiagramm (Use Case Diagram)
Das Systemverhalten wird aus der Sicht des Anwenders dargestellt. Es
wird nicht gezeigt, wie, sondern was möglich ist.
•
Aktivitätsdiagramm (Activity Diagram)
Dient der Darstellung von Geschäftsprozessen wie sie
Anwendungsfällen vorkommen, oder um komplexe Logik
modellieren.
in
zu
•
Zustandsdiagramm (State Machine Diagram)
Das Zustandsdiagramm beschreibt Zustände und Zustandsübergänge
eines Objekts – ähnlich einem endlichen Automaten.
•
Interaktionsdiagramme (Interaction Diagrams)
•
Sequenzdiagramm (Sequence Diagram)
Zeigen die sequentielle Logik, wobei in der Regel nur ein Weg
durch einen Entscheidungsbaum angegeben wird. Es werden die
Nachrichten zwischen verschiedenen Objekten in Abhängigkeit von
der Zeit angegeben.
•
Kommunikationsdiagramm (Communication Diagram)
Dieses Diagramm beschreibt den Nachrichtenaustausch zwischen
Objekten, wobei – im Gegensatz zum Sequenzdiagramm – die
interne Struktur im Vordergrund steht.
•
Interaktionsübersichtsdiagramm
(Interaction
Overview
Diagram)
Kombination aus Aktivitäts- und Interaktionsdiagrammen, wobei
die einzelnen Aktivitäten Interaktionen sind.
•
Zeitdiagramm (Timingdiagramm)
Das aus der technischen Informatik bekannte Timingdiagramm
beschreibt Zustandsänderungen in Abhängigkeit von der Zeit und
als Reaktion auf externe Ereignisse.
17
2.2 UML
Ein Diagramm besteht aus mehreren Modellelementen,
cd Klassen
die zur besseren Übersicht innerhalb eines Rechtecks
(frame) dargestellt werden können. In diesem Frame
Ein Kommentar
kann in der linken oberen Ecke Art, Name und
Parameter des Diagramms angezeigt werden. Die Art
Abbildung 7: UML
wird durch Ausschreiben des Diagrammtyps oder durch Diagramm
mit Kommentar
eine Abkürzung kenntlich gemacht (z.B. pkg für
package, sm für state machine, sd für sequence diagramm). Viele Modellelemente
können in mehreren Diagrammarten vorkommen, beispielsweise der Kommentar,
der durch ein Rechteck mit umgeknickter rechter oberer Ecke dargestellt wird.
Kommentare können in allen Diagrammarten vorkommen und können sowohl
alleine stehen als auch über eine gestrichelte Linie mit einem anderen
Modellelement verbunden werden.
Große Softwaresysteme können aus mehreren hundert oder sogar tausend Klassen
bestehen. Sie alle in einem einzigen Klassendiagramm darzustellen, wäre nicht
sehr übersichtlich. Diese Vorgehensweise war aber auch nicht von den
Entwicklern der UML beabsichtigt. Vielmehr sollten mehrere kleine Diagramme
erzeugt werden, um einzelne Komponenten des Systems zu beschreiben. Dabei ist
auch das Zusammenspiel unterschiedlicher Diagrammarten von großer Bedeutung.
Um ein großes Softwaresystem aus unterschiedlichen Blickwinkeln zu betrachten,
werden sogenannte Views (Sichten) benutzt. Dies sind Ansichten spezifischer
Aspekte, die meist aus mehreren kleinen und unterschiedlichen Diagrammen
bestehen. Oft benutzte Sichten sind:
•
Anwendungsfallsicht (use case view):
Zeigt
die
Funktionalität
aus
(Anwendungsfalldiagramm)
der
Sicht
des
Anwenders
•
Entwurfssicht (design view oder logical view):
Dient der Darstellung der statischen und dynamischen Struktur des zu
entwickelnden Softwaresystems (Klassen-, Objekt-, Zustands-, Aktivitäts- und
Kollaborationsdiagramm)
•
Prozesssicht (process view):
Benutzt die selben Diagramme wie die Entwurfssicht, jedoch stehen Aspekte
wie Nebenläufigkeit, parallele Ausführung und die Synchronisation von
Prozessen im Vordergrund. Diese Sicht wird manchmal auch concurrency view
genannt.
18
2.2 UML
•
Implementierungssicht (implementation view)
Zeigt die Organisation des Quellcodes
(Komponenten- und Model
Management Diagramme)
•
Verteilungssicht (deployment view):
Beschreibt
die
Verteilung
auf
(Verteilungsdiagramm)
unterschiedliche
Computer
Im Folgenden werden einige Modellelemente und Diagramme aufgezeigt, wobei
der Schwerpunkt auf der Darstellung von Klassen und den Beziehungen zwischen
Klassen liegt. Dabei ist zu beachten, dass in UML Farbe nicht semantisch benutzt
wird; es werden lediglich Empfehlungen gegeben, um Unterschiede zwischen
Elementen farblich hervorzuheben. Die Umsetzung bleibt aber dem Benutzer bzw.
dem Hersteller von UML-konformer Software überlassen.
Die UML-Spezifikation definiert im Wesentlichen einzelne Modellelemente.
Diese können größtenteils in unterschiedlichen Diagrammen vorkommen.
2.2.3.1
Klassen- und Objektdiagramm
Das Class Diagram oder Klassendiagramm beschreibt Klassen mit Attributen und
Methoden sowie ihre Beziehungen untereinander.
Eine Klasse wird dargestellt als Rechteck, das mittels horizontaler Linien in
mehrere Bereiche unterteilt werden kann.
Im ersten Bereich steht optional ein Klassen-Stereotyp, gefolgt vom Namen der
Klasse (fett gedruckt) sowie optional Eigenschaften, die in geschweifte Klammern
gefasst werden. Ist die Klasse abstrakt, so wird der Name in kursiver Schrift
geschrieben und die Eigenschaft {abstract} hinzugefügt. Vor dem Klassennamen
kann der Name eines Pakets gefolgt von zwei Doppelpunkten stehen.
Der zweite Bereich listet die Attribute der Klasse auf, die in folgender Syntax
verfasst sind:
[visibility] [/] name [: type] [multiplicity] [= default] [{ property-string }]
Zu Beginn steht ein Symbol für die Sichtbarkeit: “-“ für private, “+“ für public,
“#“ für protected und “~“ für package. Der Schrägstrich wird bei abgeleiteten
19
2.2 UML
Attributen gesetzt, d.h. bei Attributen, die zur Laufzeit aus anderen Attributen
ermittelt werden können. Der anschließende Name des Attributs wird gefolgt von
Typ, Multiplizität sowie einem Initialwert. Zusätzliche Eigenschaften wie
{readOnly} oder auch die Angabe eines Wertebereichs werden in geschweiften
Klammern festgehalten. Attribute, die als Typ eine Klasse haben, können auch
durch Beziehungen visualisiert werden (siehe Aggregation).
Im nächsten Bereich werden die Operationen bzw. Methoden dargestellt. Die
Syntax ähnelt der der Attribute:
[visibility] name ( [parameter-list] ) [: return-type] [{ property-string }]
Ist die Methode abstrakt, so wird der Name entweder kursiv geschrieben oder
unterstrichen. Zusätzlich kann die Eigenschaft {abstract} hinzugefügt werden. Die
Parameterliste ist eine Komma separierte Liste von formalen Parametern und
genügt folgender Syntax:
[direction] name : type [multiplicity] [= default] [{ property-string }]
direction gibt die Richtung des Parameters an. Fehlt diese Angabe, wird “in“ als
Standardwert angenommen. Zulässige Werte sind “in“, “out“ und “inout“.
Sowohl Attribute als auch Operationen können anhand der Sichtbarkeit gruppiert
werden. Zusätzliche Bereiche mit benutzerdefinierten Bemerkungen oder
Einschränkungen können folgen, jedoch sind alle Bereiche außer dem ersten
optional.
Objekte werden auf ähnliche Weise dargestellt. Im Namensbereich wird
unterstrichen der Name des Objekts gefolgt von einem Doppelpunkt und dem
Klassennamen angegeben. Anstelle der Attributsdefinitionen werden
Attributnamen mit konkreten Werten verwendet.
Beispiele für unterschiedliche Darstellungsformen von Klassen sind in
Abbildung 8 gezeigt. (a) stellt eine Klasse mit Attributnamen und -Typen sowie
einer Operation dar. In (b) werden zusätzlich Sichtbarkeit und Initialwerte
angezeigt. (c) bietet eine Sortierung der Attribute nach Sichtbarkeit ähnlich der
Syntax der Programmiersprache C++. Abbildung (d) und (e) sind
Minimaldarstellungen, bei denen nur der Klassennamen angezeigt wird und (e) ist
ein Objekt mit konkreten Werten.
20
2.2 UML
(a)
(b)
(c)
Window
Window
Window
size: Area
visibility: Boolean
defaultSize: Area
+ size: Area = (100,100)
# visibility: Boolean = true
+ defaultSize: Area
draw()
+ draw()
public
size: Area = (100,100)
defaultSize: Area
protected
visibility: Boolean = true
+ draw()
(d)
Window
(e)
Window
(f)
Ansicht1 : Window
size = (100,100)
visibility = true
defaultSize = (50,50)
Abbildung 8: UML Klassen und ein Objekt
Bei den Beziehungen zwischen Klassen und Objekten werden vier Arten
unterschieden: Assoziation, Generalisierung, Abhängigkeit und Realisation
(Schnittstellen).
Eine Assoziation stellt die allgemeinste Art einer Beziehung dar. Sie bedeutet
lediglich, dass eine Klasse irgendwie Kenntnis von einer oder mehreren anderen
Klassen haben muss. Die graphische Darstellung orientiert sich sehr an dem
Entity-Relationship-Modell nach [Chen76], das u.a. bei der Modellierung von
Datenbanken eingesetzt wird. Die nachfolgende Abbildung stellt einige Beispiele
graphisch dar. Assoziationen werden durch eine durchgezogene Linie dargestellt.
Soll die Beziehung nur in einer Richtung möglich sein, so wird die Linie an einem
Ende mit einer Pfeilspitze abgeschlossen (a). Für jede der beiden Richtungen kann
optional eine Bezeichnung angegeben werden. Die Richtung, für die diese
Bezeichnung gilt, wird mit einem ausgefüllten Rechteck, das in die gewünschte
Richtung zeigt, visualisiert (b). Diese Bezeichnung ist eine Beschreibung für die
Assoziation selbst. Zusätzlich kann man angeben, welche Rolle eine Klasse bei
einer Assoziation spielt (c). Weiterhin können Multiplizitäten angegeben werden
(d). Diese geben an, wie viele Objekte der einen Sorte mit wie vielen Objekten der
anderen Sorte in Verbindung stehen können. Formuliert wird dies als eine Zahl,
eine Menge von Zahlen, Zahlenbereiche (z.B. „1..7“) oder eine Kombination
davon (Beispiele: „1“ oder „3, 5, 7“ oder „1..7, 11“ ). Ist keine Multiplizität
angegeben, so wird „1“ als Default-Wert angenommen. Ist eine Klasse in
Assoziation mit sich selbst, so nennt man dies Rekursive Assoziation (e). Sind
21
2.2 UML
mehr als zwei Klassen in einer Assoziation involviert (n-äre Assoziation), so
werden die Linien der Assoziation mit einer leeren Raute verbunden (f). In einer
n-ären Assoziation sind weder Aggregation noch Qualifizierte Assoziation erlaubt.
Eine Qualifizierte Assoziation wird bei one-to-many und many-to-many
Beziehungen benutzt und stellt eine Art Schlüssel dar, mit dem eindeutig
zwischen den verschiedenen Elementen an dem „many-Ende“ der Assoziation
unterschieden werden kann. Der Schlüssel bzw. qualifier wird dabei in einem
(b)
benutzt
1..*
0..*
Auto
Kind Person
wird
benutzt
von
(c)
1
(a)
hat Mutter
(e) 1..*
Zug
Vorlesung
fährt mit
Auto
{xor}
Person
(i)
Firma
(h)
Fahrschein
(f)
Tag
Raum
(g)
Uni
Matrikelnr
(d)
* Student
Abbildung 9: UML Assoziationen
kleinen Rechteck dargestellt, das Klasse und Assoziation miteinander verbindet.
Dieses Rechteck sollte immer kleiner sein, als die mit ihm verbundene Klasse, um
Verwirrung zu vermeiden (Beispiel (g): Eine Universität hat für jeden Studenten
eine eindeutige Matrikelnummer). Einer Assoziation kann eine Klasse zugeordnet
werden, die dann Assoziationsklasse genannt wird. Sie ist mittels einer
gestrichelten Linie mit der Assoziation verbunden und stellt zusätzliche
Informationen für diese Beziehung zur Verfügung (h). Eine weitere Möglichkeit
für die Modellierung mit Assoziationen stellt das Xor-Constraint zur Verfügung.
An dem Namen erkennt man bereits, dass es sich dabei um eine Einschränkung
(Constraint) handelt und das Xor weist darauf hin, dass von zwei Möglichkeiten
nur genau eine verwendbar ist. Dazu ein Beispiel (i): Wir haben die drei Klassen
Auto, Person und Firma. Ein Auto wird zugelassen auf den Namen einer
Privatperson oder auf eine Firma, d.h. es gibt Assoziationen zwischen Auto und
Person sowie zwischen Auto und Firma. Ein Auto kann aber nicht auf beide
zugelassen werden. Die Lösung bietet das Xor-Constraint, das nur genau eine der
beiden Assoziationen zulässt. Dargestellt wird es mit einer gestrichelten Linie
zwischen den beiden Assoziationen und einer für Constraints in UML üblichen
Schreibweise {xor}.
22
2.2 UML
Eine Aggregation ist eine spezielle Form der Assoziation, deren beteiligte
Klassen eine Ganzes-Teil-Struktur darstellen, d.h. eine Klasse ist Teil einer
Klasse. Dabei kann das Teil auch ohne das Ganze existieren (Beispiel (a): Auto
aggregiert Reifen). Eine weitere Spezialisierung dieses Konzepts ist die
Komposition, bei der das Teil nicht ohne das Ganze existieren kann (Beispiel:
Fenster aggregiert Icon und Button (b), Haus aggregiert Zimmer). Dargestellt
werden diese beiden Assoziationen durch eine Raute, die im Fall der Aggregation
leer (a) und im Fall der Komposition ausgefüllt (b) ist. Als besondere Form von
Assoziation können Aggregation und Komposition die von Assoziationen
bekannten Elemente Rolle, Multiplizität und qualifier haben. Ist die aggregierte
Klasse Teil von mehreren aggregierenden Klassen, so nennt man die Aggregation
shared (Beispiel (c): Ein Team besteht aus Personen, aber eine Person kann auch
Mitglied in mehreren Teams sein). Visualisiert wird dies durch die Angabe einer
Multiplizität ungleich eins, die auf der aggregierenden Seite steht – also auf der
Seite, an der die Raute dargestellt wird. Sind mehrere Klassen auf der „Teil“-Seite
der Beziehung, dann können diese zusammengefasst und in einer Baumstruktur
dargestellt werden (shared target style (d), im Gegensatz zu separate target style
(b)). Soll auf die Visualisierung mit Hilfe von Assoziationen verzichtet werden,
kann die Darstellung auch mit Attributen erfolgen ((e) mit Komposition, (f) mit
Attribut).
(a)
hat
Auto
(c)
*
Team
(b)
(e)
4 Reifen
Mitglied
Window
*
Button
Person
(d)
*
Icon
size
Window
1
(f)
Window
*
Button
Area
Window
size: Area
*
Icon
Abbildung 10: UML Aggregation und Komposition
Die Generalisierung ist eine direkte Abbildung des Konzepts aus der
objektorientierten Programmierung. Eine spezielle Klasse (Subklasse oder
Unterklasse) erbt dabei alle Attribute, Operationen und Assoziationen von einer
allgemeineren Klasse (Superklasse oder Oberklasse). Dieses Konzept kann nicht
23
2.2 UML
Person
(a)
Beruf
{incomplete, overlapping}
(c)
Geschlecht
{complete, disjoint}
(b)
Mann
Frau
Mechaniker Lehrer
Vehikel
Landvehikel
{incomplete, overlapping}
Auto
(e)
(d)
Wasservehikel
{incomplete, overlapping}
Segelboot Motorboot
Amphibienfahrzeug
Abbildung 11: UML Generalisierung
auf Objekte übertragen werden und tritt deshalb im Objektdiagramm nicht auf.
Generalisierung wird dargestellt durch eine durchgezogene Linie zwischen
Superklasse und Subklasse mit einem leeren Dreieck auf der Seite der Superklasse
(a). Wie bei Aggregation kann bei mehreren Subklassen die Darstellung in einer
Baumstruktur erfolgen (b) oder sie kann durch eine gestrichelte Linie kenntlich
gemacht werden (c). Auch eine Einteilung in mehrere logische Gruppen
(Partitionen) ist möglich (Generalization Set, Beispiel (d): Landvehikel und
Wasservehikel). Die Beschreibung der Gruppe wurde in früheren UML-Versionen
Discriminator (Unterscheidungsmerkmal) genannt und wird an die zugehörige
Generalisierung geschrieben. Es gibt vier Constraints, von denen je zwei
gegensätzlich sind und nicht zusammen benutzt werden können:
complete / incomplete und disjoint / overlapping. Complete (vollständig) bedeutet,
dass keine weiteren Subklassen mehr hinzugefügt werden können (Beispiel (b): es
gibt nur die Unterscheidung zwischen Mann und Frau, weitere Unterscheidungen
wären nicht sinnvoll). Bei incomplete (unvollständig) können noch zusätzliche
Subklassen modelliert werden. Die anderen beiden Constraints betreffen die
Subklassen der Subklassen in der Vererbungshierarchie, wenn Mehrfachvererbung
möglich ist. Eine Subklasse der Subklassen kann von allen Subklassen erben, die
mit overlapping (überlappend) gekennzeichnet sind, während dies bei disjoint
(trennen) nicht möglich ist. Dazu ein Beispiel: Sei Vehikel eine generalisierte
Klasse der Klassen Auto und Motorboot, die mit dem Constraint overlapping
24
2.2 UML
versehen wurden. Dann kann es eine Subklasse Amphibienfahrzeug (e) geben, die
Auto und Motorboot als Superklasse hat.
Da die Generalisierung zwischen einem allgemeinen und einem spezielleren
Modellelement definiert ist, existiert auch eine Generalisierung von
Assoziationen.
Bei Abhängigkeiten (Dependencies) ist ein Modellelement abhängig von einem
anderen. Wird ein Modellelement – das als Supplier bezeichnet wird – geändert,
dann kann dies Auswirkungen auf das andere
A
(a)
Element – das Client genannt wird – haben.
ABCD
Dargestellt wird dies mit einem Pfeil mit einer
B
gestrichelten Linie, die vom Client zum Supplier
C
zeigt. Optional kann der Pfeil mit einem Namen und
DBCA
einem Stereotyp versehen werden, von denen einige
D
vordefiniert sind. Es ist möglich, mehrere Clients
und mehrere Supplier zu haben. In diesem Fall
Sommerreifen
zeigen die Pfeile aller Clients auf einen Punkt (der
(b)
auch als Punkt, also als kleiner ausgefüllter Kreis,
«substitute»
visualisiert werden kann), von dem aus Pfeile zu den
Ganzjahresreifen
Suppliern gehen (a). Es gibt mehrere verschiedene
Arten
von
Abhängigkeiten,
die
durch
Abbildung 12: UML
Abhängigkeiten
unterschiedliche
Stereotypen
gekennzeichnet
werden. «abstraction» wird benutzt zwischen zwei
Elementen, die dasselbe Konzept auf verschiedenen Abstraktionsstufen oder aus
unterschiedlichen Blickwinkeln repräsentieren. Einige Spezialformen von
«abstraction» sind vorgegeben (z.B. «derive», «refine», «trace»). So auch die
Realisierung, bei der der Supplier eine Spezifikation und der Client dessen
Implementierung repräsentiert. Die graphische Darstellung der Realisierung
orientiert sich an Abhängigkeiten durch eine gestrichelte Linie und an
Generalisierung durch ein leeres Dreieck auf der Seite des Suppliers
(Spezifikation). Eine weitere Abhängigkeit ist mit «permit» gegeben, durch das
der Client Zugriffsrechte auf alle Elemente des Supplieres erhält. Dies kann in
C++ durch das Schlüsselwort „friend“ erreicht werden. Durch «substitute» können
Instanzen von Klassen während der Laufzeit ausgetauscht werden, ohne dass
Generalisierung verwendet wird (b). Dies ist beispielsweise sinnvoll bei
Zielplattformen, die Vererbung und Polymorphie nicht unterstützen. Bei der
Verwendung von «use» benötigt ein Modellelement für seine Implementierung
und Ausführung andere Modellelemente.
25
2.2 UML
Eine Schnittstelle (Interface) ist eine Klasse, bei der alle Methoden abstrakt sind.
Eine solche Klasse kann nicht instanziert werden, sondern definiert nur die Namen
der Funktionalität. Eine andere Klasse (Anbieter) muss dann die Implementierung
dieser Schnittstelle liefern, die dann von einer weiteren Klasse (Nutzer) benutzt
wird. Für Schnittstellen gibt es drei mögliche Darstellungsformen. Bei der ersten
Möglichkeit (a) wird die Schnittstelle wie eine Klasse als Rechteck mit dem
Stereotyp «interface» dargestellt und ist mit dem Anbieter über eine Realisierung
und mit dem Nutzer über eine Abhängigkeit verbunden. In der zweiten
Möglichkeit (b) wird die Schnittstelle als Kreis (ball) visualisiert, die mit dem
Anbieter über eine durchgezogene Linie verbunden ist. Der Kreis wird mit dem
Namen der Schnittstelle versehen und ist mit dem Nutzer über eine Abhängigkeit
(a)
Anbieter
«interface»
Schnittstelle
(b)
Anbieter
Nutzer
Schnittstelle
(c)
Anbieter
Nutzer
(d)
Protokollierung
zur
Abrechnung
Nutzer
Schnittstelle
Pay-TV
Decoder [1]
Signal
Abbildung 13: UML Schnittstellen und Ports
(«use») verbunden. In der dritten Möglichkeit (c) wird der Anbieter und die
Schnittstelle wie zuvor dargestellt. Der Nutzer ist mit einer durchgezogenen Linie
mit einem Halbkreis (socket) verbunden, der eine benötigte Schnittstelle
repräsentiert. Diese Form nennt sich ball-and-socket Notation.
Schnittstellen beschreiben die Verbindung zwischen verschiedenen
Softwarekomponenten, aber sie sagen nichts über die Systemumgebung aus. Zu
diesem Zweck benutzt man Ports, die durch ein kleines Rechteck am Rand der
Klasse (oder auch anderer Modellelemente) dargestellt werden. Die benötigten
und unterstützten Schnittstellen werden dann an den Port angebracht und Name
sowie in eckigen Klammen Multiplizität daneben geschrieben. Im Beispiel (d)
benötigt man zum Nutzen von Pay-TV einen Decoder. Dieser benötigt ein
Fernsehsignal und bietet eine Schnittstelle an, mit der ein Unternehmen die
Nutzung protokollieren und so exakte Rechnungen stellen kann.
26
2.2 UML
Parametrisierte Klassen (Templates) sind Klassen, die noch nicht vollständig
spezifiziert sind. Erst wenn Parameter an sie gebunden, können sie instanziert
werden. Dieses Konzept wird in C++ mit Templates und in Java mit Generics
realisiert. Graphisch dargestellt werden sie durch ein Rechteck auf der rechten
oberen Ecke der Klasse (a), in dem die Parameter definiert werden. Die
T, k : int
Liste
(a)
eintrag : T [0..k]
(b)
(b)
«bind» <T→Gast, k→20>
«bind» <T→Adresse, k→50>
Gaestebuch
Telefonbuch
Abbildung 14: UML Parametrisierte Klassen (Templates)
Parameterdefinition ist eine durch Kommas separierte Liste von Parametern der
Form „name [: type] [= default]“, wobei der Typ Klasse nicht angezeigt wird
((a): „T“ hat als Typ Klasse). Gebunden werden die Parameter mit dem Stereotyp
«bind» dem eine in die Zeichen „< >“ gefasste und durch Kommas separierte Liste
mit Parametern der Form „Parametername → Wert“ folgt (b).
Der Quellcode für dieses Beispiel in C++ ist:
template <class T, int k>
class Liste {
T eintrag[k];
};
Liste<Adresse,100> Telefonbuch;
Liste<Gast,20>
Gaestebuch;
und in Java:
class Liste<T> {
Vector<T> eintrag;
public Liste(int k) {
eintrag = new Vector<T>(k);
}
}
Liste<Adresse> Telefonbuch=new Liste<Adresse>(100);
Liste<Gast>
Gästebuch =new Liste<Gast>
(20);
27
2.2 UML
Dabei sind Gast und Adresse noch zu definierende Klassen. Dieses Beispiel ist
[Jeckle04] entnommen.
Zusätzlich können Pakete im Klassendiagramm benutzt werden, die im Folgenden
betrachtet werden.
2.2.3.2
Model Management – Paketdiagramm
UML definiert drei Möglichkeiten, um Modellelemente zu gruppieren. Dabei
werden unterschiedliche Aspekte des Systems hervorgehoben.
Pakete können verschiedene Modellelemente enthalten. Dabei wird ein
eindeutiger Namensraum festgelegt, d.h. sie können beispielsweise in C++ durch
namespace realisiert werden. Graphisch dargestellt werden sie durch ein großes
Rechteck über dem ein kleines links ausgerichtetes Rechteck – das tab genannt
wird – steht. Die Elemente der Gruppe können entweder in dem großen Rechteck
untergebracht werden (a) oder außerhalb der Gruppe. In letzterem Fall werden sie
mit durchgängigen Linien mit der Gruppe verbunden, wobei am Gruppenende der
Linie ein Kreis mit einem Plus-Zeichen angebracht ist (b). Sind die Elemente in
der Gruppe, dann wird der Gruppenname im tab angezeigt (a). Ansonsten kann der
Name im großen Rechteck dargestellt werden (b). Jedes Element kann eine
Sichtbarkeit haben, die entsprechend der Sichtbarkeit von Attributen und
Operationen definiert ist und visualisiert wird ((c) und (d)). Grundsätzlich gibt es
drei Beziehungen zwischen Paketen, die in Form einer Abhängigkeit mit
unterschiedlichen Stereotypen dargestellt werden. Mit «access» erhält ein Paket
P1 Zugriff auf ein anderes Paket P2, wobei alle Elemente aus P2 in P1 die
Sichtbarkeit private haben. Durch «import» werden die Elemente aus einem Paket
in ein anderes importiert und sind auch dort von außen sichtbar. Sie erhalten also
per default die Sichtbarkeit public (Beispiel: in (c) importiert das Paket X das
Paket Y. Dabei werden nur die öffentlichen Elemente – d.h. die Elemente mit
Sichtbarkeit '+' (public) – importiert. Dies sind „C“ und „E“. In (d) ist das
Ergebnis des Imports zu sehen, wobei die importierten Elemente mit dem
eindeutigen Namen des Pakets Y markiert sind, d.h. „C“ wird zu „Y::C“). In
diesem Fall kann den importierten Elementen allerdings auch eine lokale
Sichtbarkeit und ein lokaler Alias-Name zugewiesen werden (Beispiel: in (d)
könnte „Y::C“ auch „-F“ genannt werden). Die dritte Abhängigkeit ist durch
«merge» gegeben. Dadurch können Elemente mit gleichen Namen aus
unterschiedlichen Paketen zu einem neuen Element verschmolzen werden
(Beispiel: In (e) werden die Pakete „P1“ und „P2“ mit „P3“ verschmolzen. Das
Ergebnis ist in (f) zu sehen. Dabei wurde das Element „A“, das in allen drei
28
2.2 UML
(a)
(c)
Paket
X
Klasse A
Klasse B
Y
A
«import»
B
(b)
Paket
+E
-D
(d)
+
Klasse A
+C
X
Klasse B
Y
A
Y::C
B
Y::E
(f)
P3'
«import»
+C
+E
-D
(e)
P1
P2
A
A
a : String
op1 ()
b : String
op2 ()
B
«merge»
C
«merge»
P3
A
A
a : String
b : String
op1 ()
op2 ()
B
D
C
D
Abbildung 15: UML Pakete
Paketen vorhanden war, zu einem neuen Element verschmolzen.). Auch
Templates können auf Pakete angewendet werden.
Models (a) werden benutzt, um verschiedene Aspekte des physikalischen Systems
in unterschiedlichen Abstraktionsgraden zu visualisieren. Graphisch dargestellt
werden Models wie Pakete mit dem Stereotyp «model» oder einem kleinen
Dreieck als Icon. Zur Darstellung gleicher Konzepte in verschiedenen Models
werden gestrichelte Verbindungslinien mit dem Stereotyp «trace» verwendet.
29
2.2 UML
Mit Subsystemen (b) wird ein Verhalten gruppiert. Die Darstellung entspricht der
des Pakets mit einem Icon. Das große Rechteck wird dabei aufgeteilt in zwei
Bereiche. Die externe Sicht zeigt die Dienste, die das Subsystem zur Verfügung
stellt (Spezifikation) und die interne Sicht zeigt die Realisierung mit Elementen,
die in anderen Diagrammen benutzt werden können. So kann im
Realisierungsbereich beispielsweise ein Anwendungsfalldiagramm oder
Klassendiagramm enthalten sein.
In einer hierarchischen Darstellung können Models und Subsysteme gemischt
auftreten.
Analyse
(a)
Design
(b)
Analyse
Spezifikation Realisierung
«trace»
Abbildung 16: UML Model (a) und Subsystem (b)
2.2.3.3
Weitere Diagramme
Die folgenden Diagramme sind für die weiteren Betrachtungen nicht
notwendigerweise relevant und werden deshalb der Vollständigkeit halber kurz
und mit einem Beispiel beschrieben.
Mit dem Komponentendiagramm werden Komponenten und Artefakte und ihre
Beziehungen untereinander angezeigt. Komponenten sind hierbei modulare und
austauschbare Teile des Systems mit eindeutig definierter Schnittstelle und
Artefakte sind die Umsetzungen (Manifestationen) der Komponenten, also
beispielsweise Dateien mit Quellcode oder auch ausführbare Dateien. Beide
Elemente
werden
durch
Rechtecke dargestellt mit den
«component»
Stereotypen «component» bzw. java.sql.
Titel_Impl
connection
Titel
«artifact» und speziellen Icons.
«manifest»
Eine
Abhängigkeitsbeziehung
mit dem Stereotyp «manifest»
«artifakt»
kann
Komponenten
und
Titel.jar
Artefakte
verbinden.
Eine
Abhängigkeit von Artefakt A
Abbildung 17: UML Komponenten
30
2.2 UML
nach Artefakt B kann darauf hindeuten, dass eine sprachenspezifische Beziehung
zwischen A und B besteht, und dass eine Änderung in B eine Neukompilierung
von A benötigt.
Das Verteilungsdiagramm (Deployment Diagram) zeigt auf, wie das System
auf unterschiedliche Computer verteilt ist und wie diese miteinander
kommunizieren. Das zentrale Element dieses Diagramms ist der Node (Knoten).
Nodes können Computer, Ausführungsumgebungen wie Serversoftware oder auch
«device»
Client1
: WindowsPC
«TCP/IP»
«device»
Client2
: WindowsPC
«device»
Server
«execution environment»
Fileserver
«TCP/IP»
«execution environment»
Webserver
Abbildung 18: UML Verteilungsdiagramm
Hardware zur Verbindung wie Switches und Hubs sein. Dargestellt werden Nodes
durch dreidimensionale Boxen mit einem Namen und den Stereotypen «device»
und «execution environment». Nodes sind in UML Classifier und können sowohl
als Typ als auch als Instanz dargestellt werden (Beispiel: ein Node „FileServer“
als eine Klasse von Servern mit den Instanzen „FileServer1“, „FileServer2“ usw.).
Der Name des Nodes wird dargestellt wie bei Klassen, d.h. fett gedruckt im Fall
eines Classifiers bzw. unterstrichen und gefolgt von einem Doppelpunkt und dem
Classifiernamen im Fall einer Instanz. Verbunden werden Nodes mit einfachen
Linien, die ein Stereotyp entsprechend der Art der Verbindung haben (z.B.
«TCP/IP»).
Mit dem Kompositionsstrukturdiagramm (Composite Structure Diagram,
auch Architekturdiagramm genannt) können die internen Strukturen von
Classifiern – wie beispielsweise Klassen – und das Zusammenspiel mit anderen
Systemkomponenten detaillierter beschrieben werden. Die verwendeten
Modellelemente sind u.a. Part, Port und Kollaboration. Ports sind bereits aus dem
Klassendiagramm bekannt. Mit Parts kann man darstellen, dass Instanzen des
betrachteten Classifiers eine Menge von Instanzen anderer Classifier durch
Komposition
(man
beachte
den
Namen
des
Diagramms:
Kompositionsstrukturdiagramm) beinhalten kann. Die folgende Abbildung zeigt
ein Beispiel dazu. Die Klasse Fahrrad (a) besitzt durch Komposition genau ein
31
2.2 UML
Hinterrad, das durch eine Kette mit zwei Pedalen verbunden ist. Eine Assoziation
zwischen Fahrrad und Pedal existiert ebenso. In Teil (b) der Abbildung wird
derselbe Sachverhalt auf eine andere Art und Weise dargestellt. Dadurch ist die
Angabe von Details über Hinterrad und Pedal möglich, die nur im Kontext der
Klasse Fahrrad gelten, beispielsweise die Angabe der Assoziation „Kette“. Da nur
eine Assoziation zwischen Fahrrad und Pedal existiert, wird Pedal in (b) in einem
gestrichelten Rechteck dargestellt. Die Multiplizität kann entweder wie gewohnt
in eckige Klammern gefasst werden oder in der rechten oberen Ecke des
Rechtecks Platz finden, wie es bei den Pedalen in (b) dargestellt ist.
Mit Kollaborationen wird die Zusammenarbeit von Classifiern oder
Operationen visualisiert. Dargestellt werden sie durch eine horizontale gestrichelte
Ellipse, die durch eine gestrichelte Linie in zwei Bereiche gegliedert ist. Im oberen
Bereich steht zentriert und fett der Typ (Beispiel (c): „Käufer-Handel“) oder der
Name gefolgt von einem Doppelpunkt und dem Typ (Beispiel (c): „Ankauf :
Handel“). Im unteren Bereich wird die interne Struktur der Kollaboration
dargestellt. Dabei können Kollaborationen geschachtelt auftreten (c) oder durch
Abhängigkeitsbeziehungen miteinander verbunden sein (d).
(a)
Fahrrad
Fahrrad
(b)
Hinterrad Kette Pedale 2
: Rad [1] 1
2 : Pedal
Hinterrad 1
2 Pedale
Kette
Rad
Pedal
2
1
Käufer-Handel
(c)
Käufer
Ankauf:
Handel
Verkäufer
Händler
Verkäufer
(d)
Käufer-Handel
Produzent
Händler
Konsument
Käufer
Verkauf:
Handel
Produzent
Konsument
«occure
n
ce»
Handel
Käufer
Verkäufer
Abbildung 19: UML Kompositionsstrukturdiagramm mit Parts und Kollaborationen
32
2.2 UML
Mit dem Anwendungsfalldiagramm oder Use Case Diagram wird das
Systemverhalten aus der Sicht des Anwenders dargestellt. Dabei wird die
Funktionalität stark abstrahiert, sodass nicht gezeigt wird, wie etwas funktioniert.
Stattdessen wird nur aufgezeigt, was möglich ist. Das Diagramm besteht
hauptsächlich aus Akteuren und Anwendungsfällen (use cases) sowie einigen
Beziehungen untereinander. Ein Akteur kann dargestellt werden als Klasse mit
Online-Shop
Produkt
suchen
Kunde
Produkt
bestellen
Produkt
versenden
«include» Sachbearbeiter
Bestellung
bearbeiten
Produkte
verwalten
Mitarbeiter
Lagerist
Abbildung 20: UML Anwendungsfalldiagramm
Stereotyp «actor», als eine kleine Grafik (z.B. ein Computer) oder als
Strichmännchen, wobei unterschiedliche Strichmännchen für verschiedene Arten
von Akteuren möglich sind (vergleiche [UML2Toolkit]). Anwendungsfälle
werden visualisiert durch eine Ellipse oder in Form einer Klasse mit einer kleinen
Ellipse als Icon. Sowohl Akteur als auch Anwendungsfall können Bezeichnungen
und Generalisierungen haben. Es gibt zwei Abhängigkeiten zwischen
Anwendungsfällen. Mit «extend» wird ein Anwendungsfall durch einen anderen
erweitert und bei «include» ist ein Anwendungsfall vollständig in einem anderen
enthalten.
Aktivitätsdiagramme
(Activity
Diagram)
dienen
der
Geschäftsprozessmodellierung und werden typischerweise zur Modellierung und
Verfeinerung von Anwendungsfällen benutzt. In gewisser Hinsicht sind sie eine
objektorientierte Adaption von Flussdiagrammen und haben eine Semantik, die
den Petri-Netzen ähnelt. Aktivitätsdiagramme haben üblicherweise einen oder
mehrere Start- (kleiner schwarzer Kreis) und Endpunkte (ein Kreis, in dessen
Mittelpunkt ein schwarzer Kreis liegt) sowie mehrere Aktivitäten (Rechteck mit
abgerundeten Ecken). Sie unterstützen unter anderem Verzweigungen, Parallelität,
Exception-Handling und die Teilung des Diagramms in logische Partitionen
(hierarchische und mehrdimensionale Partitionierungen sind möglich).
33
2.2 UML
Abbildung 21: UML Aktivitätsdiagramm, entnommen aus [UML2Toolkit]
Mit dem Zustandsdiagramm (State Machine Diagram) können Zustände und
Zustandsübergänge von Objekten, Schnittstellen, Anwendungsfällen u.a.
beschrieben werden. Die Zustände werden dargestellt durch ein Rechteck mit
abgerundeten Ecken und können in bis zu drei Bereiche unterteilt werden. Im
oberen Bereich ist der Name. Im optionalen mittleren Bereich wird das Verhalten
als Antwort auf bestimmte Ereignisse aufgelistet. Dabei können eigene Ereignisse
definiert werden, es sind jedoch auch schon drei Ereignisse vordefiniert. Mit entry
werden Aktionen beim Beginn des Zustands ausgeführt, mit do während des
Ausführens und mit exit definiert man die Aktionen beim Verlassen des Zustands.
Der optionale untere Bereich ist für interne Zustandsänderungen vorgesehen.
Zustandsänderungen (Transitions) werden durch Pfeile dargestellt und können
einen Namen und zusätzliche Bedingungen haben. Hat man mehrere
Zustandsdiagramme, dann können sich diese gegenseitig durch einen
Nachrichtenaustausch beeinflussen. Dargestellt wird dies durch einen gestrichelten
sm Geldautomat
defekt
{final}
defekt
Karte prüfen
{final}
Karte angenommen
Betrag wählen
Karte ausgeben
{final}
Betrag
gewählt
Trasaktion
bestätigen {final}
Abbildung 22: UML Zustandsdiagramm
34
2.2 UML
Pfeil von einem Quellobjekt zu einem Zielobjekt (Objekt bedeutet in diesem
Zusammenhang ein Zustand oder einen Zustandsübergang) zusammen mit einer
Nachricht, die vom Zielobjekt verstanden werden muss. Desweiteren gibt es den
History Indicator. Dieser wird benutzt, um interne Zustände zu speichern, falls zu
einem späteren Zeitpunkt zu diesem Zustand zurückgekehrt werden muss
(entsprechend einem Abbruch in einem Algorithmus). Dargestellt wird er durch
einen kleinen Kreis in dessen Mittelpunkt ein „H“ bzw. ein „H*“ steht, je
nachdem ob der Zustand dieses Diagramms oder rekursiv die Zustände aller
umschließenden Zustandsdiagramme wiederhergestellt werden sollen. Weitere
Elemente sind Anfangs- (kleiner schwarzer Kreis) und Endpunkt (Kreis mit
schwarzem Kreis in der Mitte), Verzweigungen (leeren Raute) und
Abbruchpunkte (Kreis mit „X“).
Im Sequenzdiagramm (Sequence Diagram) steht die Zusammenarbeit und der
Nachrichtenaustausch zwischen Objekten in Abhängigkeit von der Zeit im
Zentrum der Betrachtung. Vertikal verläuft die Zeit und horizontal werden die
Objekte aufgetragen. Jedes Objekt hat eine sogenannte Lebenslinie (gestrichelte
Linie), die vertikal unter dem Objekt angebracht ist. Wird ein Objekt für die
weitere Betrachtung nicht mehr benötigt, dann endet die Lebenslinie mit einem
großen Kreuz. Nachrichten zwischen Objekten werden durch Pfeile zwischen
deren Lebenslinien dargestellt. Wird eine Nachricht an ein Objekt gesendet, dann
beginnt eine Aktionssequenz, die durch ein schmales Rechteck über der
Lebenslinie kenntlich gemacht wird. Die komplette Sequenz ist eingefasst in ein
Abbildung 23: UML Sequenzdiagramm, entnommen aus [UML2Toolkit]
35
2.2 UML
großes Rechteck, in dessen linker oberer Ecke ein Operator und optional eine
Beschreibung angebracht ist. Der Operator gibt die Art der Sequenz an. Dabei
steht beispielsweise „sd“ für sequence diagram und „alt“ für Alternative.
Letzteres kann in vielen Programmiersprachen mittels einer „if“ - Anweisung
realisiert werden. Weitere Operatoren sind verfügbar (loop, neg, critical, ...).
Zeitangaben sind ebenso möglich und werden durch Constraints realisiert.
Das Interaktionsübersichtsdiagramm (Interaction Overview Diagram) zeigt
den Zusammenhang zwischen verschiedenen Interaktionen in Form eines
Aktivitätsdiagramms. Dabei sind die einzelnen Aktivitäten Interaktionen wie
beispielsweise Sequenz- oder Kommunikationsdiagramme, die mit den aus dem
Aktivitätsdiagramm bekannten Modellelementen miteinander verbunden werden.
iod Geldautomat
ref
PIN-Eingabe
[PIN == falsch]
sd
:Authentifizierungs:Display
system
ref
Karteneinschub
[PIN == richtig]
ref
Geldtransaktion
ref
Karteneinzug
anzeigen („Karte
wird eingezogen“)
ref
Kartenauswurf
Abbildung 24: UML Interaktionsübersichtsdiagramm
Das aus der technischen Informatik bekannte Zeitdiagramm (Timingdiagramm)
beschreibt Zustandsänderungen von Klassen, Akteuren, Komponenten und
anderen Modellelementen und Diagrammen in Abhängigkeit von der Zeit und als
36
2.2 UML
Reaktion auf externe Ereignisse. Dadurch ist eine präzise Darstellung von
zeitkritischen Systemen möglich. Die zu betrachtenden Instanzen werden dabei
vertikal und die Zeit horizontal angeordnet. Im unteren Bereich ist eine Zeitskala
darstellbar und Nachrichten können zwischen den Instanzen durch Pfeile
dargestellt werden.
: Ampel
td Fußgängerampel
{4*d}
grün
rot
betriebsbereit
gehen
nicht
gehen
: Fußgänger
aktivieren
aktiv
wartend
0
10
20
Sek.
30
Abbildung 25: UML Timingdiagramm
Das Kommunikationsdiagramm (Communication Diagram) ist eine
Weiterentwicklung
des
aus
frühreren
UML-Versionen
bekannten
Kollaborationsdiagramms. Es beschreibt – wie das Sequenzdiagramm – den
Nachrichtenaustausch zwischen Objekten, wobei allerdings die interne Struktur
sowie der Nachrichtenaustausch und nicht die zeitliche Abfolge im Vordergrund
steht. Um zeitliche Aspekte zu berücksichtigen, können Nachrichten mit einer
Nummerierung versehen werden.
cd Gassi gehen
1: Gassi gehen
Hundehalter
Hund
3: entfernen
2.1: schimpfen
1.1: machen
2: reintreten
Passant
Häufchen
Abbildung 26: UML Kommunikationsdiagramm
37
Kapitel 3 Stand der Technik
In diesem Kapitel sollen einige graphisch orientierte Programme vorgestellt und
getestet werden. Dabei wird untersucht, ob ein vorhandener Quellcode korrekt
eingelesen und dargestellt werden kann, ob die Darstellung zum Verständnis
geeignet ist und diverse Optionen zulässt – wie beispielsweise das Ausblenden
von Details und farbliche Unterstützung – und ob der Quellcode aus der
Darstellung richtig erzeugt wird. Weiterhin werden die Programme auf einige
nützliche Funktionen hin untersucht, wie das Vorhandensein einer Zoom-Funktion
und die Möglichkeit, die komplette graphische Darstellung in einem kleinen
Übersichtsfenster anzuzeigen.
Weitere Informationen sind in Form einer tabellarischen Zusammenstellung in
Anhang B: Stand der Technik zu finden.
3.1
Programm: ClassBuilder 2.4 Alpha 1.7
ClassBuilder ist ein Opensource-Projekt, das auf Sourceforge ([W3-SF]) als
Freeware erhältlich ist und unter Windows läuft. Der Sourcecode wird unter der
zlib/libpng-Lizenz vertrieben. Die unterstützte Programmiersprache ist C++.
Das Reverse Engineering ist über einen speziellen Menüpunkt aufrufbar. Nach der
Angabe verschiedener Pfade sucht das Programm nach Dateien mit vorher
festgelegten Dateiendungen. Der Sourcecode wird dabei nicht korrekt erkannt,
obwohl das Programm keinen Fehler meldet. Einer der Fehler, die gefunden
wurden, hat seine Ursache in der Instanzierung von Objekten. In C++ wird ein
Objekt mit dem Namen der Klasse und anschließenden Namen des Objekts
angelegt. Optional kann das Schlüsselwort „class“ angegeben werden (Beispiel für
die Instanzierung des Objekts Paul für die Klasse Mensch: „Mensch Paul;“ bzw.
„class Mensch Paul;“). Wird das Schlüsselwort „class“ angegeben, dann wird der
Sourcecode ab dieser Stelle nicht mehr richtig erkannt. So ist im Test auch eine
Klasse mit dem Namen „)“ erkannt worden, was in C++ sicherlich nicht möglich
ist. Weitere Fehler treten bei der Verwendung von Templates auf.
Ist der Sourcecode eingelesen, werden die erkannten Klassen mit Attributen und
Methoden in einer Baumstruktur dargestellt (siehe Abbildung: linkes Fenster).
Dort können Attribute und Methoden von einer Klasse in eine andere Klasse
38
3.1 Programm: ClassBuilder 2.4 Alpha 1.7
verschoben werden. Wird vorhandener Code mehrmals eingelesen, tauchen
Attribute und Methoden fälschlicherweise mehrmals in der Baumstruktur auf.
Abbildung 27: Screenshot ClassBuilder 2.4 Alpha 1.7
Durch einen Menüpunkt werden Klassen- und Sequenzdiagramme erstellt. Welche
Klassen und welche Attribute bzw. Methoden dieser Klassen dargestellt werden,
kann individuell für jedes Diagramm ausgewählt werden. Leider werden nur
Generalisierungen angezeigt, aber keine Assoziationen. Aggregationen können
erstellt werden, haben jedoch keinen Einfluss auf den erzeugten Sourcecode.
Automatisches Layout und eine Zoom-Funktion sind vorhanden, jedoch kein
Übersichtsfenster. Ein simpler Texteditor ist integriert, bietet aber nur wenige
Funktionen.
Dokumentationen mit Beschreibungen zu allen Klassen sowie die erstellten
Diagramme können in RTF und HTML exportiert werden.
Der erzeugte Sourcecode unterscheidet sich beträchtlich vom eingelesenen Code.
Überall werden Kommentare mit unklarer Bedeutung eingefügt (z.B.
„//@CODE_352“) und es werden neue Methoden mit den Namen
39
3.1 Programm: ClassBuilder 2.4 Alpha 1.7
„ConstructorInclude“ und „DestructorInclude“ erstellt. Der komplette Code wird
neu formatiert. Dabei gehen vorhandene Einrückungen verloren. Die Reihenfolge
der Methoden geht ebenfalls verloren, da diese alphabetisch sortiert werden.
Methodenkörper werden beibehalten. Wird ein Attribut oder eine Methode
innerhalb der Baumstruktur in eine andere Klasse verschoben, dann äußert sich
dies auch im Sourcecode in der Klassendefinition, allerdings wird dies nicht im
Methodenkörper berücksichtigt. Für jede Klasse wird eine separate Datei angelegt,
auch wenn im ursprünglichen Code mehrere Klassen in einer Datei waren.
3.2
Programm: Jumli 1.4
Die Studenten der Berufsakademie der Bausparkasse Schwäbisch Hall erstellten
Jumli als UML-Editor und Entwicklungsumgebung für Java, C# und C++. Jumli
ist primär als Schulungstool konzipiert und kostenlos unter www.jumli.de
erhältlich. Externe Compiler und Debugger können in das Programm durch
Eingabe des Pfades eingebunden werden.
Durch Öffnen der Sourcecode-Dateien und anschließendes Analysieren werden
diese in einer Baumstruktur dargestellt (siehe Abbildung: linkes Fenster). Der
Sourcecode wird dabei nicht richtig erkannt. So wurde im Test eine Methode als
Klasse gefunden. Auch die Sichtbarkeitsidentifizierer „private“ und „protected“
hält das Programm für Klassen. Anscheinend hat Jumli Probleme beim Reverse
Engineering von Templates und von Klassen mit Nested Klassen.
Es können Klassen-, Sequenz-, Use-Case-, Kollaborations-, Aktivitäts-, Zustandsund Komponenten-Diagramme erstellt werden. Durch einfaches Drag & Drop
können Klassen aus der Baumstruktur in das Klassendiagramm übernommen
werden. Für einzelne Klassen oder eine Menge von markierten Klassen können
Attribute und Methoden wahlweise ausgeblendet und eine Farbe festgelegt
werden. Für Aggregationen und Kompositionen gibt es nur eine Darstellung.
Diese entspricht der UML-Aggregation. Ein Texteditor ist vorhanden. Dort
können einzelne Passagen per Mausklick ein- und auskommentiert werden. Eine
Zoom-Funktion ist sowohl für Text als auch für die Diagramme verfügbar. Leider
ist bei höheren Zoom-Stufen der Bildschirmaufbau recht langsam. Ein
Übersichtsfenster existiert nicht und automatisches Layout ist nicht möglich.
40
3.2 Programm: Jumli 1.4
Abbildung 28: Screenshot Jumli 1.4
Generalisierung und Assoziation können durch einfaches Ziehen einer Linie
zwischen zwei Klassen erstellt werden. Aggregationen werden in einem
Eigenschaftsfenster der Klasse eingetragen.
Der aus dem Modell generierte Sourcecode weist nahezu keine Veränderungen im
Vergleich zum ursprünglichen Code auf. Neue Attribute können ohne Probleme
erstellt werden und scheinen auch in Code korrekt angelegt zu werden. Nach dem
Löschen von Attributen sind diese in Jumli nicht mehr sichtbar, werden jedoch
nicht aus dem Sourcecode entfernt. Wird ein Klasse, ein Attribut oder eine
Methode umbenannt, dann wird die alte Benennung komplett beibehalten und
einfach ein zusätzliches neues Element erzeugt.
Die Daten können als XMI und HTML exportiert werden.
41
3.3 Programm: Metamill v3.1 (build 556)
3.3
Programm: Metamill v3.1 (build 556)
Metamill gibt es als 30tätige Testversion mit einer Einschränkung von 20
Elementen pro Diagramm. Die unterstützten Programmiersprachen sind sowohl
für Reverse Engineering als auch für Forward Engineering Java, C++, ANSI C
und C#.
Beim Importieren von vorhandenen Sourcecode wurde lediglich ein Fehler
gemeldet. Die betreffende Datei wurde dann übersprungen. Alle anderen Klassen,
Attribute und Methoden wurden korrekt erkannt. Allerdings sind keine
Aggregationen und Kompositionen gefunden worden, sondern nur
Generalisierungen. Klassen und Assoziationen werden getrennt in einer
Baumstruktur verwaltet.
Abbildung 29: Screenshot Metamill v3.1 (build 556)
42
3.3 Programm: Metamill v3.1 (build 556)
Metamill unterstützt Use-Case, Klassen-, Sequenz-, Paket-, Statechart,
Kollaborations-, Aktivitäts-, Komponenten- und Verteilungsdiagramme, die in
den drei Sichten Use-Case, Design und Implementierung angeordnet sind. Klassen
können durch Drag & Drop aus der Baumstruktur in ein Klassendiagramm
übernommen werden. Eine farbliche Abgrenzung ist vorhanden, kann aber nicht
beeinflusst werden. Zoom ist möglich. Zur Darstellung der Klassen gibt es zwei
Detailstufen: entweder es werden alle Attribute und Methoden mit allen Details
angezeigt oder es werden nur die öffentlichen angezeigt, wobei bei den Methoden
die Parameter unterdrückt werden. Diese Einstellung gilt nur für das gesamte
Diagramm und ist nicht auf einzelne Klassen anwendbar. Diagramme können als
Windows Enhanced Metafile (.emf) gespeichert, und Projekte im XMI-Format
importiert und exportiert werden.
Generalisierungen, Assoziation, Aggregationen und noch weitere Beziehungen
können durch einfaches Ziehen einer Linie von einer Klasse zu einer anderen
erstellt werden.
Der große Nachteil von Metamill äußert sich im Forward Engineering.
Methodenkörper werden nicht in die mit Metamill erstellten Dateien
übernommen. Zusätzlich ist der erzeugte Code überfüllt mit kryptischen
Kommentaren (z.B. „//#UBLK-BEG-METHOD mm:6384627a-ef21-11d8-9839f3f647b836dc“). Erstellte Aggregationen werden nicht in den generierten
Quellcode übernommen und umbenannte Elemente finden sich ebenfalls nicht im
Code wieder.
3.4
Programm: ObjectDomain R3 (build 292)
Da ObjectDomain in Java programmiert wurde, läuft es auch auf allen Javakompatiblen Plattformen. Dabei werden die Sprachen Java, C++, Python und IDL
unterstützt.
Im rechten unteren Bereich ist ein Ausgabefenster. Dort werden nach dem Reverse
Engineering
diverse
Exceptions
angezeigt
(z.B.
„java.lang.ArrayIndexOutOfBoundsException“ in „Tokenizer.java“). Tatsächlich
wurden einige Attribute und Methoden nicht erkannt.
Nach dem Einlesen des Sourcecodes werden erkannte Klassen in der gewohnten
Baumstruktur angezeigt und können von dort mittels Drag & Drop in ein erstelltes
43
3.4 Programm: ObjectDomain R3 (build 292)
Diagramm übernommen werden. Zoom, Übersichtsfenster und automatisches
Layout sind vorhanden. Für jede Klasse kann einzeln ausgewählt werden, ob der
Attributebereich oder Methodenbereich angezeigt werden soll. Dies geht jedoch
nicht für eine Gruppe von Klassen. Aggregationen und Kompositionen werden als
Pfeil dargestellt und durch einfaches Ziehen von einer Klasse zu einer anderen
erstellt.
Das Umbenennen von Elementen funktioniert. Allerdings werden Kompositionen
als Aggregationen erstellt und im erzeugten Sourcecode werden Methodenkörper
nicht übernommen.
Abbildung 30: Screenshot ObjectDomain R3 (build 292)
ObjectDomain unterstützt Use-Case-, Klassen-, Objekt-, Komponenten-,
Verteilung-, Statechart-, Aktivitäts, Sequenz- und Kollaborations-Diagramme, die
in den Bildformaten png, gif, jpg, ppm, svg und wmf exportiert werden können.
Import und Export von XMI und Rational Rose-Dateien (.mdl) ist möglich.
44
3.5 Programm: objectiF 4.7
3.5
Programm: objectiF 4.7
Das von microTOOL angebotene Programm objectiF wird in drei Versionen
angeboten. Die objectiF Visual Studio .NET Edition bietet eine Integration in
Microsoft Visual Studio .NET und ist speziell für die Entwicklung in Visual Basic
.NET optimiert. Die objectiF Eclipse Edition ist für die Java-Entwicklung unter
Eclipse bestimmt. Die hier getestete Version ist die objectiF Enterprise Edition,
die als Standalone für die Programmiersprachen C++, Java und Visual Basic .NET
gedacht ist. Zusätzlich werden die Eclipse Edition und die Visual Studio .NET
Edition in einer kostenlosen Personal Edition angeboten, die jedoch auf fünf
Diagramme pro Diagrammart begrenzt ist.
Objectif definiert mehrere Sichten, denen einzelne Diagramme zugeordnet sind.
Die Anforderungssicht enthält Aktivitäts- und Use-Case-Diagramme, für das
Design können Aktivitäts-, Klassen-, Paket- und Sequenzdiagramme angelegt
werden, die Implementierungssicht benutzt Klassen- und Sequenzdiagramme und
die Systemsicht ist aus Paket-, Aktivitäts-, Use-Case-, Klassen- und
Abbildung 31: Screenshot objectiF 4.7
45
3.5 Programm: objectiF 4.7
Sequenzdiagrammen aufgebaut. Ergänzend zu einzelnen Klassen können
Zustandsdiagramme angelegt werden.
Das Reverse Engineering läuft ohne Probleme ab. Lediglich ein Fehler wurde
gemeldet und dieser steht in Zusammenhang mit einer speziellen Erweiterung von
Microsoft Visual C++, die nicht konform mit dem C++ - Standard ist. Die
gefundenen Klassen werden dann in einer Baumstruktur verwaltet und können
durch Drag & Drop in ein erstelltes Klassendiagramm eingefügt werden. Nicht
alle Beziehungen werden korrekt angezeigt bzw. erkannt.
Attribute und Methoden von Klassen können per Mausklick angezeigt und
ausgeblendet werden. Zoom ist vorhanden, jedoch fehlt ein automatisches
Anordnen. Auch Kompositionen sind nicht vorhanden. Alle Elemente haben einen
semantischen Namen und eine Deklaration bzw. einen technischen Namen.
Ersteres ist nur für die Darstellung in objectiF, während die Deklaration angibt,
wie das Element im Sourcecode benannt wird. Aus einem Klassendiagramm kann
ein sogenanntes Kontextdiagramm erstellt werden, d.h. zu einer gegebenen Klasse
wird ein neues Klassendiagramm erstellt mit eben dieser Klasse und allen
Klassen, die damit in Beziehung stehen. Es ist möglich, den Sourcecode zu
manipulieren (allerdings nur an bestimmten Stellen). Änderungen werden aber
erst sichtbar, nachdem der Code erzeugt und erneut mittels Reverse Engineering
eingelesen wurde.
ObjectiF bietet mehrere Refaktorisierungs-Möglichkeiten an. Wird die Funktion
„Interface extrahieren“ auf eine Klasse angewendet, so öffnet sich ein Fenster, in
dem mehrere Methoden der Klasse ausgewählt werden können. Es wird ein
Interface mit diesen Methoden erstellt und eine Beziehung angelegt, die anzeigt,
dass die Klasse das Interface realisiert. Durch die Möglichkeit „Superklasse
extrahieren“ bzw. „Unterklasse extrahieren“ wird eine neue Klasse mit vorher
ausgewählten Methoden erstellt, so dass die vorhandene Klasse von dieser neuen
Klasse erbt bzw. als Superklasse für eine neue Unterklasse fungiert. Durch
„Klasse extrahieren“ können einzelne Methoden aus einer vorhanden Klasse in
eine neue Klasse ausgelagert werden.
Durch einen Rechts-Klick auf den Punkt „Dateien“ in der Baumstruktur kann das
Forward Engineering gestartet werden. Dadurch wird der Sourcecode neu
geschrieben und auch geändert, wenn in objectiF keine Änderungen
vorgenommen wurden. So wird die Reihenfolge der Attribute und Methoden
verändert, und es werden Kommentare eingefügt. Wurden im ursprünglichen
46
3.5 Programm: objectiF 4.7
Code mehrere Attribute in einer Anweisung definiert (z.B. „int i,j;“), dann werden
diese aufgespalten und separat definiert („int i; int j;“).
Das Umbenennen von Elementen wird anscheinend korrekt durchgeführt. Dabei
werden die Namen überall im Code angepasst. Wird ein Element entfernt, so wird
nur seine Deklaration entfernt, jedoch bleiben vorhandene Codezeilen, in denen
dieses Element auftritt, erhalten. So erhält der Entwickler die Möglichkeit, beim
erneuten Kompilieren anhand der Fehlermeldungen – die durch das nicht
definierte Element auftreten – jedes Vorkommen einzeln zu überprüfen.
3.6 Programm: Rational Rose Enterprise Edition
(2003.06.00.436.0)
Rational Rose wurde ursprünglich von der Firma Rational Software entwickelt,
der Geburtsstätte der Unified Modelling Language (UML) und könnte somit als
Referenzprodukt gelten.
Beim Anlegen eines neuen Projekts hat der Benutzer die Auswahlmöglichkeit
zwischen den verschiedenen Sprachen und Dialekten J2EE, J2SE 1.2 - J2SE 1.4,
JDK 1.1.6, JDK 1.2, JFC 1.1, Oracle 8 Datentypen, RUP, Visual Basic 6, Visual
C++ 6 ATL 3.0 und Visual C++ 6.0 MFC 6.0. Es stehen die Sichten Use Case,
Logisch, Komponenten und Verteilung zur Verfügung sowie Use Case-, Klassen-,
Komponenten-, Verteilung-, Interaktions- und Zustands-Diagramme.
Das Reverse Engineering ist etwas kompliziert zu finden. Dazu muss zuerst eine
neue Komponente erstellt und dieser eine Sprache zugewiesen werden. Als
Sprachen stehen u.a. ANSI C++, Java, Visual Basic, Ada83, Ada95, CORBA und
Oracle8 zur Auswahl, wobei im Test ANSI C++ gewählt wurde. Danach muss im
Menü der Punkt „ANSI C++ Spezifikation“ geöffnet werden, wo ein Pfad
eingestellt und Dateien hinzugefügt werden können. Dabei werden die
angegebenen Dateien bereits nach vorhandenen Klassen untersucht. Anschließend
werden über den Menüpunkt „ANSI C++ Reverse Engineer“ die Klassen
ausgewählt, die eingelesen und dem Modell hinzugefügt werden sollen. Es ist
möglich, automatisch Pakete entsprechend den Unterverzeichnissen, in denen die
Klassen definiert sind, anzulegen.
Während dem Reverse Engineering wurden keine Fehler gemeldet. Die
gefundenen Klassen sowie die Beziehungen zwischen ihnen werden in einer
47
3.6 Programm: Rational Rose Enterprise Edition (2003.06.00.436.0)
Baumstruktur angezeigt. Sie können per Drag & Drop in ein erstelltes
Klassendiagramm gezogen werden, wobei allerdings jedes Element einzeln dem
Diagramm hinzugefügt werden muss. Für das Klassendiagramm stehen Zoom,
Übersichtsfenster und ein automatisches Layout zur Verfügung. Die Farbe kann
für einzelne Klassen oder für eine Gruppe von Klassen geändert werden. Zur
Darstellung kann jederzeit zwischen den Notationen der Methoden Booch, OMT
und Unified Method sowie zwischen unterschiedlichen Detaillierungsgraden
ausgewählt werden.
Das Umbenennen von Klassen, Attributen und Methoden wird direkt im
Diagramm vorgenommen. Neue Beziehungen erreicht der Benutzer durch
einfaches Ziehen der entsprechenden Linie.
Abbildung 32: Screenshot Rational Rose Enterprise Edition
Das Forward Engineering wird für einzelne Klassen durchgeführt über das
Kontextmenü, das durch einen Rechtsklick auf die entsprechende Klasse (sowohl
in der Baumstruktur als auch im Diagramm) erhältlich ist. Für eine Gruppe von
48
3.6 Programm: Rational Rose Enterprise Edition (2003.06.00.436.0)
Klassen bedient der Anwender sich des Kontextmenüs der zu Beginn erstellten
Komponente.
Im erzeugten Sourcecode wird vor jede Klasse, jedes Attribut und jede Methode
ein Kommentar gesetzt, beispielsweise „//##ModelId=408338D900FC“. Wurde
eine Klasse umbenannt, so wird eine neue Klasse mit dem neuen Namen erstellt.
Diese hat alle Methoden und Attribute der Klasse mit dem alten Namen, es
werden jedoch nur Methodenrümpfe ohne Code erzeugt. Verweise auf die Klasse
werden korrekt auf den neuen Namen gesetzt. Die Klasse mit dem alten Namen
kann auf Wunsch gelöscht werden.
Wird ein Attribut umbenannt, so wird der Name in der Deklaration korrekt
geändert, jedoch wird kein Auftreten des Attributs in irgend einer Methode
verändert. Methodenkörper werden also nicht angepasst.
3.7
Programm: WithClass 2000 Enterprise 6.0
Welche Version dieses Programms zum Test benutzt wurde, ist nicht ganz klar.
Die Download-Möglichkeit auf der Homepage des Herstellers MicroGold benennt
sie mit „WithClass 2000 Enterprise 6.0“. Trotzdem meldet sich das Programm mit
einer Version 8.5 (siehe Titelleiste in der Abbildung).
WithClass unterstützt eine große Anzahl von unterschiedlichen Methoden und
Programmiersprachen. Dabei kommen die Methoden Unified Method, Rumbaugh,
Coad/Yourdon, Booch, Shlaer/Mellor und Martin/Odell zum Einsatz, die auf
Klassen-, Sequenz-, Statechart-, Use Case-, Kollaborations- und
Implementierungs-Diagramme angewendet werden können. Als Sprachen können
C++, Java als Quellcode (.java) und im Bytecode (.class), Delphi, Visual Basic,
IDL, C# und VB.NET eingelesen werden. Zur Ausgabe stehen Ada, C++, C#,
Delphi, Eiffel, IDL, Java, OO Cobol, Smalltalk, SQL, Visual Basic .NET, Visual
Basic, HTML und Visual Foxpro zur Verfügung.
Das Reverse Engineering ist durch zwei getrennte Menüpunkte durchführbar. Mit
„Reverse File“ bzw. „Reverse Directory“ können C++ - Header-Dateien
eingelesen werden. Durch den Punkt „Import all Code“ werden danach in einem
zweiten Schritt die zugehörigen Methoden verarbeitet. Dabei können optional
Fehler ausgegeben werden, die jedoch nicht allzu aufschlussreich sind, da nur das
entsprechende Token benannt wird, nicht jedoch Dateiname und Zeilennummer,
49
3.7 Programm: WithClass 2000 Enterprise 6.0
in denen der Fehler auftrat.
Die gefundenen Klassen werden in einer Baumstruktur verwaltet. Nested Klassen
werden wie normale Klassen behandelt. Nach dem Reverse Engineering wird
automatisch ein neues Klassendiagramm mit allen Klassen erstellt. Obwohl dabei
ein Layout aller Klassen durchgeführt wird, ist ein automatisches Layout zu einem
späteren Zeitpunkt nicht mehr möglich. Lediglich horizontales bzw. vertikales
Anordnen sowie ein Anordnen von Elementen durch Verschieben, so dass die
Elemente gleichen Abstand besitzen, ist für eine markierte Gruppe von Klassen
möglich. Zoom ist ebenfalls möglich, hat aber im Test manchmal zu Problemen
geführt, so dass das ganze Betriebssystem Aussetzer von mehreren Minuten hatte.
Ein Übersichtsfenster ist vorhanden. Die Farbe, in der die Klassen dargestellt
werden, kann für das ganze Diagramm oder nur für einzelne Klassen verändert
werden, nicht jedoch für Gruppen von Klassen. Ebenso verhält es sich mit der
Anzeige von Details. Hier stehen mehrere Auswahlmöglichkeiten zur
Verfügungen, so dass Sichtbarkeit, Typ und weitere Werte der Attribute und
Abbildung 33: Screenshot WithClass 2000 Enterprise 6.0
50
3.7 Programm: WithClass 2000 Enterprise 6.0
Methoden beliebig an- und abgeschaltet werden können. Die Anzeige kann auf
Elemente mit bestimmter Sichtbarkeit (public, protected, private) beschränkt
werden oder auch nur auf den Klassennamen. Weiterhin besteht die Möglichkeit,
die Anzahl der angezeigten Attribute und Methoden auf fünf zu beschränken
(andere Werte sind nicht möglich). Ein Text-Editor mit Syntax-Highlighting ist
vorhanden. Die Rauten am Ende einer Aggregation enden nicht immer direkt an
einer Klasse. Ob dies ein Bug oder ein Feature ist, sei dahingestellt. Die benutzte
Methode kann jederzeit geändert werden, und das Diagramm wird dann in der
entsprechenden Notation neu gezeichnet. Der Export von Diagrammen ist in den
Bildformaten bmp, gif, jpg, tiff und wmf möglich.
Die Namen von Klassen können direkt im Diagramm geändert werden. Neue
Attribute und Methoden erstellt der Benutzer in einem Dialogfenster, das sich
durch Doppelklick auf eine Klasse öffnet. Hier können diese Elemente auch
umbenannt werden. Beziehungen wie Generalisierung und Aggregation können
durch einfaches Ziehen der entsprechenden Linie erstellt werden. Zusätzliche
Assoziationen sind vorhanden, werden allerdings nur durch ihr Aussehen
beschrieben und sind anscheinend nur zum Design vorhanden. Diese
Assoziationen werden ausgewählt durch Icons in der Toolbar. Leider sind diese
Icons doppelt vorhanden (einmal in der Toolbar am oberen Rand des
Klassendiagramms und einmal in der Toolbar des Programms am linken
Fensterrand), so dass die Übersicht darunter leidet.
Beim Forward Engineering kann der Anwender zwischen den verschiedenen
unterstützten Sprachen auswählen. Dabei ist das Erstellen der Header-Dateien
vom Erstellen der Source-Dateien mit den Implementierungen der Methoden
getrennt, so dass wie beim Reverse Engineering das Erstellen in zwei separaten
Schritten erfolgen muss. Dabei wird für jede Klasse und Struktur eine separate
Datei angelegt. Auch Nested Klassen werden in einer separaten Datei
untergebracht. Die Sourcecode-Dateien werden umformatiert, so dass vorhandene
Einrückungen verloren gehen. Auch vorhandene Kommentare werden nicht in den
neu erzeugten Code übernommen. Dafür werden neue Kommentare eingeführt.
Wurden Elemente in WithClass umbenannt, so werden sie auch in den
entsprechenden Deklarationen umbenannt, der restliche Code wird jedoch nicht
angepasst. Enumeratoren werden komplett entfernt. Die „#inlcude“-Anweisungen
werden verändert und teilweise entfernt. Einige Attribute werden doppelt erstellt.
51
3.8 Weitere Programme
3.8
Weitere Programme
Es wurden noch weitere Programme getestet, die hier aber nur kurz vorgestellt
werden sollen:
Select Component Factory 5.0
Dieses Programm greift sehr stark in das Betriebssystem ein. So werden u.a.
mehrere neue Benutzer unter Windows erstellt und einige speicherresidente
Programme gestartet. Select Component Factory kann sich in Visual Age,
JBuilder 4 - JBuilder 7, Eclipse und WebSphere Studio Application Developer
(WSAD) integrieren und definiert die Diagramme Process Hierarchy, Process
Thread, Use Case, Class, Object Collaboration, Object Sequence, Activity, Entity
Relationships, Table Relationships, State, General Graphics und Text.
Kernstück des Engineering ist ein separates Programm namens „Select C++
Sync“. Dort kann der Benutzer Sourcecode in ein Modell einlesen und nach dem
Bearbeiten wieder in Code verwandeln. Bei jedem Start dieses Programms wird
der Code neu eingelesen und die gefundenen Elemente den Elementen des
Modells gegenübergestellt. Pfeile vom Modell zum Code oder umgekehrt deuten
auf eine Veränderung hin, und es kann mit dem Reverse oder Forward
Engineering begonnen werden. Dies muss leider für jede Klasse einzeln
durchgeführt werden.
Wird ein Attribut, eine Klasse oder eine Methode in Select Component Factory
umbenannt, dann meldet das Synchronisierungstool ein neues Element im Modell
und ein neues Element im Code. Infolge dessen werden immer nur neue Elemente
hinzugefügt. Wird ein Attribut oder eine Methode gelöscht, dann ändert sich
nichts im Code.
Es werden lediglich
Beziehungen.
Generalisierungen
angezeigt,
aber
keine
anderen
UMLStudio 7.1 (Build 746)
UMLStudio unterstützt die Notationen UML (Klassen-, Use-Case-, Sequenz-,
Kollaborations-, Zustand-, Aktivitäts- und Implementierungs-Diagramm), Booch
(Klassen, Use-Case, Sequenz, Kollaborations-, Zustand-, Prozess- und ModulDiagramm), und Strukturiert (ER-, Use-Case-, Sequenz-, Kollaborations-, Data
52
3.8 Weitere Programme
Flow-, Flow Chart- und Jackson-Diagramm).
Es werden Links, Places und Diagramme definiert. Places sind Klassen und
Anwendungsfälle u.a. und Links sind die Beziehungen zwischen ihnen. Zu jedem
Diagramm kann festgelegt werden, welche Links und Places vorhanden sein
dürfen. Das Aussehen der Links ist veränderbar.
Roundtrip Engineering wird nicht angeboten. Es kann zwar ein Modell aus
vorhandenem Code erzeugt werden, und aus dem Modell kann wieder Sourcecode
generiert werden, aber Methodenkörper werden nicht übernommen.
Der Hersteller bietet auf der Homepage eine Buch über C++-Programmierung
kostenlos zum Download an.
Enterprise Architect 4.0 build 729
Dieses Programm unterstützt alle UML 2.0 Diagramme sowie zusätzlich die
Diagramme
„Extended
Class“
und
„Analysis“
(vereinfachtes
Aktivitätsdiagramm). Es gibt die Sichten Use Case, Dynamisch, Logisch,
Komponenten, Verteilung und Benutzerdefiniert.
In Enterprise Architect gibt es keine Möglichkeit, vorhandene C++-Dateien
einzulesen, außer den Header-Dateien. So werden auch Methodenkörper nicht
übernommen. Die Codegenerierung kann minimal angepasst werden durch
vorgegebene Generierungs-Templates. Jedes Diagramm kann als Desgin Pattern
eingesetzt werden.
In Anbetracht der Tatsache, dass nur Header-Dateien verarbeitet werden, gibt es
eine große Anzahl von Funktionen mit fragwürdigem Nutzen, wie die Verwaltung
eines Glossars und eine Rechtschreibprüfung für Kommentare.
53
3.9 Fazit
3.9
Fazit
Keines der hier vorgestellten Programme lief ohne Fehler ab bzw. konnte durch
einfaches Reverse Engineering mit anschließendem Forward Engineering den
ursprünglichen Sourcecode unverändert wiederherstellen. So wurden
Einrückungen geändert, kryptische Kommentare eingeführt oder gar ganze
Klassen in separate Dateien ausgelagert. Teilweise ist der Code nach der
Verarbeitung nicht mehr kompilierbar. Eine Refaktorisierung sollte mit diesen
Programmen also nur vorgenommen werden, wenn der Benutzer eine Änderung
der Struktur der Sourcecodes in Kauf nehmen und zusätzliche Zeit investieren
kann, um den Code wieder lauffähig zu machen. Zum Erstellen von
Dokumentationen und zum Design neuer Komponenten können sie jedoch sehr
nützlich sein.
Bei allen Programmen werden die Klassen in einer Art Baumstruktur verwaltet.
Die graphische Darstellung ist getrennt von dieser Verwaltung. So können in
einem Diagramm Beziehungen geändert oder hinzugefügt werden, ohne dass dies
Einfluss auf den vorhandenen Code hätte. Dadurch kann der Benutzer seiner
Kreativität freien Lauf lassen, um ein vorhandenes Design neu zu gestalten.
Die unterschiedlichen Optionen für graphische Darstellungen, wie beispielsweise
individuelle Farbeinstellungen und die Unterdrückung von Details durch das
Ausblenden
von
Attributen,
Methoden
sowie
Sichtbarkeit,
Typ,
Methodenparameter u.a. unterstützen das direkte Erkennen von vernetzten
Strukturen, obwohl eine zusätzliche detailliertere Sichtweise mit Berücksichtigung
der an speziellen Beziehungen beteiligten Methoden von Vorteil wäre.
Weitere Darstellungsmöglichkeiten wie das Sequenzdiagramm werden lediglich in
der Erstellung unterstützt, insofern als dass dort für die Nachrichten zwischen
einzelnen Objekten eine Auswahl aus den vorhandenen Methoden der Klasse zur
Verfügung steht. Eine automatische Erstellung anhand einer Methode mit
rekursiver Verzweigung aller aufgerufenen Objekte wäre wünschenswert.
54
Kapitel 4 Diskussion und Konzept
UML ist sehr vielseitig und unterstützt die Software-Entwicklung in allen Phasen,
jedoch bereitet eine Abbildung in neuen bzw. aus vorhandenem Sourcecode einige
Probleme. Der Grund hierfür liegt in der hohen Abstraktion und Allgemeinheit der
Modellierungssprache. Als Beispiel sei das Anwendungsfalldiagramm genannt.
Dieses kann sicherlich ohne weitere Informationen weder in Code übersetzt noch
aus gegebenem Code gewonnen werden. Auch andere Diagramme können
teilweise in Sourcecode abgebildet werden, aber eine automatische Erstellung von
UML-Diagrammen anhand von Sourcecode ist problematisch oder gar unmöglich.
So kann beispielsweise ein endlicher Automat in Code übersetzt werden, aber
beim Reverse Engineering in das entsprechende Diagramm taucht u.a. die Frage
auf, wie ein Zustand im Programm realisiert wurde (z.B. durch eine Zahl, eine
Klasse oder durch Enumeratoren).
Dies wird auch im offiziellen UML-Standard deutlich gemacht:
„The UML, a visual modeling language, is not intended to be a
visual programming language, in the sense of having all the necessary visual
and semantic support to replace programming languages“
Auch wenn UML nicht zur graphischen Programmierung konzipiert wurde,
können doch viele Konzepte und graphische Darstellungen für diesen Zweck
benutzt werden. Und sie sollten auch benutzt werden, da UML ein anerkannter
und beliebter Standard ist, der ständig weiterentwickelt wird.
Bevor ein Konzept entwickelt wird, sollten zuerst einige Eigenschaften und
Probleme der Zielsprache C++ aufgezeigt werden.
4.1
Die Programmiersprache C++
Als Nachfolger der Programmiersprache B ([W3-B72]) wurde 1972 von Dennis
Ritchie bei AT&T Bell Laboratories die erste Version der Programmiersprache C
entwickelt. 1980 wurde C von Bjarne Stroustrup um objektorientierte Konzepte
erweitert. Dies wurde „C mit Klassen“ genannt. Erst 1983 wurde der Name in
„C++“ geändert. Dabei steht „++“ für den Imkrement-Operator, der den Wert
einer Variablen um eins erhöht.
55
4.1 Die Programmiersprache C++
1998 wurde C++ von der International Standards Organisation ISO unter der
Bezeichnung „ISO/IEC 14882-1998(E)“ standardisiert.
Einige der besonderen Eigenschaften von C++, die beim Roundtrip Engineering
Probleme bereiten können, sind:
C++ enthält C
C++ ist eine Erweiterung von C und enthält alle syntaktischen und semantischen
Eigenschaften von C. Somit sind noch einige „Altlasten“ vorhanden, die die
Verarbeitung komplizieren (siehe hierzu auch Kapitel 5.1 Parser, Präprozessor).
Vorverarbeitungsstufe (Präprozessor)
Vor dem Parsen wird der Sourcecode durch eine Vorstufe verändert. Weitere
Informationen hierzu sind in Kapitel 5.1.2 Präprozessor
Zeiger und Pointerarithmetik
Pointer oder Zeiger zeigen auf einen Speicherbereich. Dies kann benutzt werden,
um nicht den Wert eines Datenelements (z.B. Variable oder Objekt) anzugeben,
sondern die Position im Speicher, an der dieses Element abgelegt ist. Mit Pointern
kann man auch rechnen. Hat man beispielsweise einen Pointer auf das zweite
Element aus einem Array von Bytes und man verändert den Pointer durch
einfache Addition mit eins, dann erhält man einen Pointer auf das dritte Element
aus dem Array. Die Verwendung von Zeigern als Datentypen wird in Kapitel
4.2.1 Datentypen, Zeiger und Referenzen besprochen.
Eigene Datentypen
Mit Hilfe des Schlüsselwortes „typedef“ können neue Datentypen definiert
werden. Dabei werden lediglich neue Namen für bereits bestehende Datentypen
vergeben.
4.2
Die graphische Darstellung
Die objektorientierte Programmierung bietet bereits eine gute Abstraktion des
Sourcecodes durch die Reduzierung ganzer Klassen auf einen Namen. Jedoch
wird eine graphische Darstellung bei mehreren hundert Klassen selbst bei dieser
Reduzierung sehr unübersichtlich. Andererseits ist es manchmal von Vorteil,
durch die Visualisierung der Verbindungen einzelner Methoden die vernetzten
Strukturen sichtbar zu machen. All diese Details in einer einzelnen Graphik
56
4.2 Die graphische Darstellung
unterzubringen, ist nicht praktikabel. Aus diesem Grund sollte ein Programm
mehrere Diagramme mit unterschiedlichem Detailgrad darstellen können. Auch
sollte es einen passenden Kompromiss zwischen Abstraktion und Sprachennähe
geben. Orientiert man sich zu sehr an einer den Eigenschaften einer bestimmten
Programmiersprache, so kann die graphische Darstellung nicht auf andere
Sprachen ausgeweitet werden. Abstrahiert man hingegen zu stark, können
Diagramme nicht mehr automatisch erstellt und aktualisiert werden. Dies führt
dazu, dass zu viel Zeit investiert werden muss, um eine Graphik zu einem
bestimmten
Programmteil
zu
erstellen
und
diese
Graphik
bei
Programmänderungen mit dem Sourcecode konsistent zu halten.
4.2.1
Datentypen, Zeiger und Referenzen
Jede Variable hat einen Datentyp und jede Methode liefert einen Rückgabewert,
der einen Datentyp besitzt. Als Datentypen stehen primitive Datentypen (u.a. int,
char, float), Objekte und selbst definierte Typen sowie Zeiger (Pointer) und
Referenzen auf diese zur Verfügung.
Beispiele für Variablen vom Typ int sowie Zeiger darauf:
01
02
int
int *
Zahl;
pZahl;
03
04
05
*pZahl = 4;
int *
pZahl2
int *
pZahl3
06
int **
07
08
09
int
& refZahl
= Zahl;
int * & refpZahl = pZahl;
int ** & refppZahl = ppZahl;
= pZahl;
= & Zahl;
ppZahl;
In Zeile (1) wird eine Zahl – d.h. eine Variable mit Datentyp int – definiert.
Zeile (2) legt einen Zeiger auf eine Zahl fest. Dabei ist „pZahl“ der
Variablenname, der auf eine Position im Speicher zeigt (auch Adresse genannt),
an der eine Zahl steht. Diese Zahl kann man ändern, indem man dem
Variablennamen ein „*“ voranstellt (Zeile (3)). Will man jedoch den Zeiger
verändern, so dass „pZahl“ auf einen anderen Speicherbereich zeigt, dann
57
4.2 Die graphische Darstellung
geschieht dies durch Zuweisung einer anderen Adresse. Dazu kann entweder ein
anderer Zeiger verwendet werden (Zeile (4)), oder man ermittelt die Adresse einer
gegebenen Zahl. Letzteres wird mit Hilfe des sogenannten Adressoperators „&“
realisiert, der die Adresse einer Variablen liefert (Zeile (5)).
Nicht nur einfache Zeiger sind möglich. In Zeile (6) wird eine Variable namens
„ppZahl“ vom Typ int** definiert, d.h. ppZahl zeigt auf eine Adresse im
Speicher, an der ein weiterer Zeiger steht, der dann auf eine Zahl zeigt. Dies kann
man beliebig fortführen.
Eine weitere Besonderheit in C++ stellen die Referenzen dar. Mit Referenzen
kann man einer existierenden Variablen oder Objekt einen zweiten Namen geben,
weshalb Referenzen auch Alias genannt werden. Angelegt werden sie mit einem
„&“ zwischen Datentyp und Variablennamen und müssen denselben Datentyp
haben wie die Variable, die sie referenzieren (Zeile (7) – (9)). Referenzen müssen
bei der Definition initialisiert werden.
Variablen stehen im Speicher und Zeiger zeigen auf eine Position im Speicher.
Zur Darstellung ist also eine Visualisierung des Speichers nützlich. Eine übliche
Darstellung dafür ist eine Band mit einzelnen Speicherzellen (siehe Abbildung,
Teil (a)). Zeiger können dann wie in (b) gezeigt dargestellt werden. Auch eine
kompaktere Form wie in (c) ist möglich. Referenzen sind zusätzliche Namen für
bereits existierende Bezeichner. Eine Visualisierung ist in (d) gegeben.
(a)
(b)
int
(d)
int
(c)
int
Abbildung 34: Zeiger und Referenzen
4.2.2
Module und Komponenten
Diese Programmteile stellen die größte Verallgemeinerung dar und sind nur
schwierig oder gar nicht aus vorhandenem Programmcode zu extrahieren.
Meistens bestehen Komponenten aus mehreren Klassen und Dateien und können
auch kleinere Komponenten beinhalten.
58
4.2 Die graphische Darstellung
Anhaltspunkte für die Zuordnung von Programmteilen zu einzelnen Komponenten
können sein:
•
•
•
Unterverzeichnisse
Namensräume
Logische Aufteilung
Bei großen Systemen werden aus Gründen der Übersichtlichkeit meistens die
Dateien, die zu einer Komponente gehören, in einem entsprechenden
Unterverzeichnis abgelegt. Auch die Verwendung von Namensräumen (in C++
durch den Befehl „namespace“ realisierbar) kann hierzu dienen. Da in beiden
Fällen ein eindeutiger Namensraum festgelegt wird, kann die Darstellung mit
Hilfe von UML-Paketen erfolgen.
Das Programm in logische Komponenten
aufzuteilen ist hingegen schwieriger. Hierbei
können graphentheoretische Verfahren behilflich
sein. Sind beispielsweise zwei Gruppen von
Klassen nur durch eine bzw. wenige Klassen
oder Assoziation miteinander verbunden, dann
kann man eine Komponente vermuten. Die
Darstellung ist beispielsweise mit UMLSubsystemen möglich.
Abbildung 35: Logische
Komponenten
Eine automatische Erzeugung nach vorgegebenen Parametern ist möglich, aber
ohne Benutzer-Interaktion in Form von Nachfragen und Vorschlägen durch das
Programm ist eine sinnvolle Unterteilung schwierig und nicht eindeutig.
4.2.3
Klassen
Klassen können direkt aus einem objektorientiert programmierten Sourcecode
extrahiert werden. Zur Darstellung bieten sich UML-Klassendiagramme an.
Zusätzlich können UML-Pakete zur Visualisierung der Namensräume und
Subsysteme zur Darstellung der Unterverzeichnisse, in denen sich die Klassen
befinden, herangezogen werden. Das Ausblenden von Details – wie das
Unterdrücken der Anzeige von Attributen bzw. Methoden – sowie die farbliche
Unterscheidung von Gruppen von Klassen dienen der einfachen Erkennung.
Modifiziert werden kann der Sourcecode und das Diagramm durch Hinzufügen
von Klassen und Assoziationen. Hierbei gibt es mehrere Möglichkeiten. Es könnte
59
4.2 Die graphische Darstellung
eine Toolbar geben, in der man zwischen Klassen und diversen Assoziationen
wählen kann. Wählt man eine Klasse aus, dann wird sie angelegt durch einfachen
Mausklick auf einen freien Bereich im Diagramm. Will man eine Assoziation
anlegen, dann geschieht dies durch Ziehen einer Linie mit der Maus von einer
Klasse zu einer anderen. Es könnten auch (möglicherweise erst nach einem Druck
einer bestimmten Taste) am Rand des Rechtecks, das eine Klasse darstellt,
mehrere Icons eingeblendet werden. Sinnvoll wären hier beispielsweise ein
Symbol
für
eine
Generalisierung
am
oberen
Rand
oder
Aggregationen/Kompositionen am Rand des Attribut-Bereichs. Klickt man auf
eines dieser Symbole und zieht es auf eine andere Klasse, dann wird die
entsprechende Assoziation angelegt.
Einige Abweichungen zum UML-Standard könnten in Betracht gezogen werden.
So ist die Zuordnung von einzelnen Zeichen zur Beschreibung der Sichtbarkeit
(„+“, „-“ und „#“) zwar sehr einfach, aber man muss die UML-Notation gut
kennen, um diese Darstellung in den richtigen Kontext zu bringen. Hier wäre eine
Verwendung von kleinen Graphiken angebracht. Häufig anzutreffen sind zwei
kleine Symbole: ein geschlossenes Vorhängeschloss für private, ein kleiner
Schlüssel für protected. Für public wird kein Symbol verwendet.
Auch die Unterscheidung von Aggregationen und Kompositionen ist nicht sehr
offensichtlich, so dass man sich manchmal fragt, ob nun Aggregation bzw.
Komposition durch eine ausgefüllte Raute oder durch eine nicht ausgefüllte Raute
dargestellt wird. Hier bietet sich eine Darstellung wie für Variablen aus dem
Kapitel 4.2.1 Datentypen, Zeiger und Referenzen an. Da diese Darstellung jedoch
sehr detailreich ist, ist sie für das Klassendiagramm nur bedingt geeignet und eine
Umschaltung auf UML-Notation sollte möglich sein.
Ein weiterer Mangel an UML-Klassendiagrammen ist die Darstellung von
Funktionsaufrufen. Es gibt eine allgemeine Assoziation, aber diese ist nur
zwischen zwei Klassen definiert. Allgemein sind alle Beziehungen nur zwischen
zwei Klassen definiert, aber es wird keine genaue Aussage darüber gemacht,
welcher Teil einer Klasse an dieser Beziehung beteiligt ist. So werden alle
Assoziationen, Aggregationen/Kompositionen und Generalisierungen als Linien
gezeichnet, die irgendwo am Rechteck der einen Klasse anfangen und irgendwo
am Rechteck der anderen Klasse aufhören. Um die Zuordnung einzelner
Klassenelemente zu diesen Assoziationen zu verdeutlichen, sollten die Linien
auch an den entsprechenden Elementen anfangen und aufhören. So könnte eine
Generalisierung immer am oberen Rand der spezialisierten Klasse beginnen und
60
4.2 Die graphische Darstellung
am unteren Rand der allgemeineren Klasse enden. Aggregationen und
Kompositionen werden direkt mit den entsprechenden Attributen verbunden. Und
Funktionsaufrufe – dargestellt durch gerichtete Assoziationen – gehen von einer
Methode zu der Methode, die durch sie aufgerufen werden. Denkbar ist auch, dass
Funktionsaufrufe immer von einem rechten Rand an einen linken Rand gehen,
entsprechend für die Methode, die eine andere Methode aufruft. In diesem Fall
wird das Diagramm aber im Ganzen gesehen unübersichtlicher, da es oft
vorkommen kann, dass eine Assoziation nur über Umwege gezeichnet wird.
Ein Beispiel dazu: Ein Fenster
(a)
Button
Window
(Klasse „Window“) hat als
Element einen Button (Klasse
draw()
draw()
„Button“). Beide haben eine
Funktion namens „Draw“, mit
der sie dargestellt werden, (b)
Button
Window
wobei die Draw-Funktion des
Fensters die Draw-Funktion
draw()
draw()
des Buttons aufruft. In UML
kann dies mit einer gerichteten
Assoziation
dargestellt
Abbildung 36: Funktionsaufrufe in Klassendiagrammen
werden, deren Anfangs- und
Endpunkt nur durch den Rand der Klassen festgelegt sind. Aussagekräftiger ist
hier jedoch die Verbindung der beiden Draw-Funktionen. Entsprechend der im
deutschen Sprachraum üblichen Leserichtung von links nach rechts sollte die
Assoziation aus der Klasse „Window“ am rechten Rand austreten und in die
Klasse „Button“ am linken Rand eintreffen. Da die Klassen evtl. durch andere
Randbedingungen des Klassendiagramms – wie beispielsweise ihre Beziehungen
zu anderen Klassen oder Paketen – an ihre Position gebunden sind, muss die
Assoziation für den Funktionsaufruf unter Umständen mit einem Umweg
gezeichnet werden. Tritt dieser Fall öfter auf, dann kann das Diagramm recht
unübersichtlich werden, es ist aber in Detailfragen aussagekräftiger.
Werden die Funktionsaufrufe korrekt dargestellt, dann ist eine Ablaufverfolgung
möglich, d.h. es werden ausgehend von einer Methode rekursiv alle Verbindungen
zu den aufgerufenen Methoden mit einer bestimmten Farbe kenntlich gemacht.
Dadurch erhält man einen schnellen Überblick über alle an einer Methode
beteiligten Elemente. Das Problem, das dabei auftaucht, sind virtuelle Methoden.
Dies sind Methoden, bei denen zur Laufzeit das Objekt auf eine überschriebene
Methode hin untersucht wird und die Methode der tiefsten abgeleiteten Klasse
61
4.2 Die graphische Darstellung
aufgerufen wird. Beispiel:
01
02
03
04
05
06
07
08
09
10
11
12
class Basis {
..
virtual void out() { printf("Basis"); }
};
class Sub : Basis {
..
void out() { printf("Sub"); }
};
Basis *obj = new Sub();
obj->out();
Es werden zwei Klassen definiert: eine Basisklasse „Basis“ und eine abgeleitete
Klasse „Sub“. Eine Methode „out“ ist in Basis definiert und wird in „Sub“
überschrieben. Anschließend wird eine Variable „obj“ vom Typ „Basis“ erstellt
und ihr ein Objekt der Klasse „Sub“ zugewiesen. Dieses Konzept wird
Polymorphie genannt. Ruft man nun die Methode „out“ auf, dann ist das
Verhalten abhängig davon, ob die Methode virtuell definiert wurde. Ist sie nicht
virtuell, dann wird Basis::out aufgerufen, denn die Variable „obj“ ist vom Typ
„Basis“. Ist die Methode jedoch virtuell definiert, dann wird zur Laufzeit ermittelt
welche Klasse vorliegt, und dann die abgeleitete Methode aufgerufen. In diesem
Fall wäre das Sub::out, denn der Variablen „obj“ wurde ein Objekt der Klasse
„Sub“ zugewiesen.
Ist eine Variable definiert als Objekt einer Klasse A, dann können dieser
Variablen Objekte von allen Klassen zugewiesen werden, die von A abgeleitet
sind. Es ist also bei Klassen mit virtuellen Methoden nicht möglich, anhand des
Datentyps einer Variablen die aufzurufende Methode festzustellen.
In diesem Fall müssen bei einer Ablaufverfolgung alle überschriebenen Methoden
gekennzeichnet werden.
62
4.2 Die graphische Darstellung
4.2.4
Methoden
Zur Darstellung der Methodenkörper eignen sich Programmablaufpläne, die auch
Flussdiagramme genannt werden. Diese stammen bereits aus den 60er Jahren und
sind in der DIN 66001 ([W3-DIN-PAP]) genormt. Besonders hilfreich ist dies bei
der Darstellung von verschachtelten Strukturen. Die wesentlichen Elemente sind:
•
•
•
•
•
Rechteck: Operation (process, z.B.: „i = 10;“)
Rechteck mit doppelten vertikalen Linien: Unterprogramm (predefined
process)
Oval: Beginn, Ende sowie weitere Grenzstellen (terminal oder interrupt,
beispielsweise return, break, continue)
Kreis: Übergangsstelle (connector)
Raute: Verzweigung (decision, z.B.: if, switch)
Zur Verdeutlichung vernetzter Zusammenhänge sollten am Rand – oder auch in
der Darstellung selbst – die aufgerufenen Methoden in Form eines UMLKlassensymbols mit Klassennamen und dem Namen der Methode angezeigt
werden. Zur Unterscheidung der Zusammenhänge der Elemente und der
vernetzten Aufrufe sollten Verbindungspfeile in unterschiedlichen Farben
dargestellt werden.
Start
i=
sort
Button
1
draw()
2
return
sonst
i = 1;
Abbildung 37: Methoden
63
4.3 Synchronisation mit Sourcecode
4.3
Synchronisation mit Sourcecode
Durch die graphische Darstellung werden viele Informationen mit den
Sprachelementen verknüpft, die nicht in der Sprache vorgesehen sind. Dies sind
Informationen über das Vorhandensein in einzelnen Diagrammen, die
Koordinaten innerhalb der Diagramme, Daten über die angezeigten Details und
weiteres.
Es gibt mehrere Möglichkeiten, dies im Sourcecode unterzubringen:
•
Makros
Werden die Makros auf eine leere Zeichenkette abgebildet, dann wird das
Programm dadurch nicht verändert. So kann beispielsweise das Funktionsähnliche Makro „VISBLE_IN_DIAGRAM“ innerhalb einer Klasse mit dem
Namen eines Diagramms als Parameter Informationen über das Vorhandensein
dieser Klasse in unterschiedlichen Diagrammen enthalten.
•
Methoden und Variablen
Wie Makros können auch Variablen und Methoden, die ansonsten nicht im
Sourcecode genutzt werden, Daten enthalten.
•
Einrückungen
Durch Einrückungen und Abstände zwischen Sprachelementen können
Informationen kodiert werden. Allerdings wird der Code dadurch unleserlich
und nur eine kleine Änderung kann unerwartete Auswirkungen auf die
graphische Darstellung haben.
•
Kommentare
Kodierung der Informationen in Kommentaren.
Weitere Möglichkeiten sind denkbar. In allen Fällen jedoch wird der Sourcecode
geändert. Er wird erweitert, teilweise sogar unleserlich und unübersichtlich. Aus
diesem Grund ist es sinnvoll, die genannten Informationen separat zu speichern.
Wird dann der Sourcecode außerhalb des Programms verändert, ist eine
Synchronisation mit den vorhandenen Informationen nötig.
Hier ist eine Gegenüberstellung sinnvoll. Die vorhandenen Daten werden mit den
neu gewonnen Informationen aus den modifizierten Dateien verglichen und
Änderungen dargestellt. Dies kann beispielsweise durch zwei Baumstrukturen
realisiert werden, wobei alle Inkonsistenzen aufgezeigt werden. Der Benutzer
64
4.3 Synchronisation mit Sourcecode
erhält dadurch die Möglichkeit, alte Bezeichnungen den außerhalb des Programms
umbenannten Elementen zuzuordnen und gelöschte bzw. neu angelegte Elemente
in das Modell zu übernommen.
4.4
Design
Neben der Implementations-Sicht sollte es noch eine weitere Sicht geben: die
Design-Sicht. Hier können neue Elemente oder Schablonen für häufig benutzte
Konstruktionen entworfen werden ohne diese direkt der Implementierung
hinzuzufügen. Dazu können alle vorherigen Diagramme genutzt werden. Die
Namen können mit speziellen Sonderzeichen ausgestattet werden, die dann mittels
Nachfrage durch einen anderen Text ersetzt werden.
Ein Beispiel dazu: Das Sonderzeichen zur Textersetzung sei „?“. Es sind die
Klassen „C?View“ und „C?Document“ mit diversen Methoden und Attributen in
einem Design gegeben. Durch die Eingabe eines Namens durch den Benutzer –
beispielsweise „Text“ werden die Klassen „CTextView“ und „CTextDocument“
dem Programm hinzugefügt.
65
Kapitel 5 Implementierung
In diesem Kapitel werden sowohl Möglichkeiten zur effizienten Realisierung als
auch die konkrete Implementierung der prototypischen Umsetzung behandelt.
5.1
Parser, Präprozessor
Eine der wichtigsten Komponenten ist der Parser (engl. to parse = analysieren).
C++ ist eine formale Sprache und kann somit durch eine Chomsky-Grammatik
ausgedrückt werden. Obwohl der Sprachumfang nicht durch eine kontextfreie
Sprache beschrieben werden kann, wird doch – wie bei vielen anderen
Programmiersprachen – eine kontextfreie Grammatik angegeben. Der Grund
hierfür ist in der effizienten Analyse dieser Grammatiken zu finden. In einem
anschließenden Schritt werden kontextsensitive Konzepte verarbeitet.
Das Erstellen eines Ableitungsbaums für die Grammatik für einen gegebenen
Sourcecode ist die Aufgabe des Parsers. Er verarbeitet nacheinander mehrere
Dateien oder Übersetzungseinheiten (translation unit). Dies geschieht in mehreren
Schritten:
1. Zuordnung von Zeichen, Trigraph-Sequenzen
Die Zeichen des Sourcecodes werden einer internen Darstellung zugeordnet.
Dadurch können bestimmte ausländische Zeichen verarbeitet werden.
Weiterhin werden Trigraph-Sequenzen aufgelöst. Dies sind Zeichenfolgen, die
als Ersatz für ein bestimmtes Zeichen stehen. Trigraph-Sequenzen stammen
noch aus einer Zeit, in der noch nicht alle benötigten Zeichen auf den
Tastaturen vorhanden waren. Im einzelnen sind dies:
Trigraph
Trigraph
??=
Entstehendes
Zeichen
#
??<
Entstehendes
Zeichen
{
??/
\
??>
}
??'
^
??!
|
??(
[
??-
~
??)
]
66
5.1 Parser, Präprozessor
2. Zusammensetzen von Zeilen
Alle Zeilen, die mit einem Backslash („\“) enden, bei denen also der Backslash
unmittelbar von einem Zeilenumbruchzeichen gefolgt wird, werden mit der
folgenden Zeile zusammengefasst. So werden aus physikalischen Zeilen
logische Zeilen. Der Backslash und das Zeilenumbruchzeichen werden dabei
gelöscht.
3. Bilden von Präprozessor-Token
Der Sourcecode wird aufgeteilt in Leerzeichen und Präprozessor-Token. Ein
Token ist allgemein die kleinste sinngebende Einheit einer
Programmiersprache. In diesem Schritt werden allerdings zuerst PräprozessorToken gebildet, die von einer Vorstufe – dem sogenannten Präprozessor –
verarbeitet werden. In Schritt 7 werden diese dann in Token umgewandelt. Die
Bildung von Präprozessor-Token ist kontextabhängig. So wird beispielsweise
das Zeichen „<“ abhängig vom Kontext einmal als Punktuator und einmal als
Teil einer „#include“-Anweisung aufgefasst. Präprozessor-Token können sein:
Präprozessor-Token
Beispiele
Header-Name
<stdio.h>
Identifier
ich_bin_1_variable
Zeichen
'c'
Zeichenketten
„Resistance ist futile“
Präprozessor Punktuator oder Operator
##
Präprozessor-Nummer
0ash12.E+
<
>
{
...
12e-OFFSET
alles andere
Die Präprozessor-Nummer (pp-number) wird später in eine Zahl umgewandelt
(Integer, Oktal, Hexadezimal, Gleitkommazahl). Tatsächlich ist aber weit mehr
als nur gültige Zahlen darstellbar (z.B: „0ash12.E+“). In diesem Punkt
unterscheidet sich der Standard von einigen Compilern wie beispielsweise
Visual C++, bei denen nur dann eine Präprozessor-Nummer gebildet wird,
wenn diese auch in eine gültige Zahl übersetzt werden kann.
Die Erstellung von Token wird mit einem Programmteil durchgeführt, das
Scanner oder Lexer genannt wird.
67
5.1 Parser, Präprozessor
Laut Standard werden in diesem Schritt auch Kommentare durch Leerzeichen
ersetzt. Da Kommentare aber nützliche Informationen sind, die in einer
(graphischen) Darstellung nicht fehlen sollten, wird an dieser Stelle vom
Standard abgewichen.
Zeilenumbruchzeichen werden beibehalten.
4. Vorverarbeitung
Präprozessor-Direktiven werden ausgeführt und Makros werden expandiert
(siehe hierzu auch Kapitel 5.1.2 Präprozessor). Bei einer #includeAnweisung werden die Schritte eins bis vier rekursiv für die entsprechende
Datei durchgeführt.
5. Zeichensatzzuordnung
Zeichen und Zeichenketten werden verändert. Escape-Sequenzen (z.B. „\n“)
werden verarbeitet.
6. Zeichenfolgenverkettung
Aufeinander folgende Zeichenketten werden zusammengefasst zu einer
einzigen Zeichenkette.
7. Analyse
Präprozessor-Token werden in Token umgewandelt. Der entstandene
Tokenstrom wird syntaktisch und semantisch analysiert (siehe hierzu Kapitel
5.1.3 Analyseverfahren).
5.1.1
Alternative Darstellungen
Für einige Operatoren und Punktuatoren existieren alternative Darstellungen. Die
Bedeutung ist dieselbe, aber die Schreibweise ist unterschiedlich:
alternativ
<%
primär
{
alternativ
bitand
primär
&
alternativ
not
primär
!
%>
}
and
&&
not_eq
!=
<:
[
bitor
|
and_eq
&=
:>
]
or
||
or_eq
|=
%:
#
xor
^
xor_eq
^=
%:%:
##
compl
~
68
5.1 Parser, Präprozessor
5.1.2
Präprozessor
Präprozessor-Anweisungen (Direktiven) sind Befehle, die den Sourcecode vor
dem Erstellen des Ableitungsbaums verändern. Die Fähigkeiten des Präprozessors
beschränken sich auf das Ersetzen von Text und stellen trotzdem ein sehr
mächtiges, aber auch fehleranfälliges Werkzeug dar. Jede Anweisung steht in
einer einzigen Zeile, die mit dem Zeichen '#' begonnen werden muss.
Der Präprozessor kann in folgende Bereiche eingeteilt werden:
•
•
•
•
Symbolische Konstanten und Makros
Dateien einfügen
Bedingte Kompilierung
Weitere Direktiven
5.1.2.1
Symbolische Konstanten und Makros
Hierfür sind die Direktiven #define und #undef zuständig. Beispiele:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
#define
#define
#define
#define
#define
DEBUG
PI 3.14159
SUB(a, b) a - b
TOSTRING(x) #x
VAR(y) variable ## y
int main()
{
int variable1, variablexy;
std::cout << PI
std::cout << SUB(10, 5) * 2
std::cout << TOSTRING(Hallo)
std::cout << VAR(1)
std::cout << VAR(xy)
}
<<
<<
<<
<<
<<
std::endl;
std::endl;
std::endl;
std::endl;
std::endl;
Mit #define kann ein Name und ein Ersatztext angegeben werden, wobei jedes
Auftreten des Namens durch den Ersatztext ersetzt wird. Es hat sich eingebürgert,
dass Makronamen in Großbuchstaben geschrieben werden. In Zeile (1) wird
lediglich ein Name angegeben, der in der bedingten Kompilierung abgefragt
werden kann und im Sourcecode durch eine leere Zeichenkette ersetzt wird. Zeile
69
5.1 Parser, Präprozessor
(2) definiert eine symbolische Konstante (auch „Objekt-ähnliches Makro“
genannt) und Zeile (3) ein Makro (auch „Funktions-ähnliches Makro“ genannt).
Bei letzterem ist darauf zu achten, dass direkt auf den Namen eine geöffnete
Klammer folgt, da sonst die Definition eines Objekt-ähnlichen Makros
angenommen wird.
Für Makros existieren zwei Operatoren. Der stringize-Operator „#“ in Zeile (4)
sorgt dafür, dass das folgende Makro-Argument durch eine Zeichenkette (String)
ersetzt wird, d.h. mit Anführungszeichen versehen wird. Mit dem concat-Operator
„##“ in Zeile (5) werden zwei gegebene Token zu einem neuen Token
verschmolzen.
Der Präprozessor liefert für obiges Beispiel folgendes Ergebnis:
07
08
09
10
11
12
13
14
15
int main()
{
int variable1, variablexy;
std::cout << 3.14159
<<
std::cout << 10 - 5 * 2
<<
std::cout << "Hallo"
<<
std::cout << variable1
<<
std::cout << variablexy
<<
}
std::endl;
std::endl;
std::endl;
std::endl;
std::endl;
Hier wird ein Problem deutlich, das auf den ersten Blick nicht erkennbar war. In
Zeile (11) war offensichtlich beabsichtigt, zwei Zahlen zu subtrahieren und das
Ergebnis dann mit zwei zu multiplizieren. Wird das Makro jedoch an dieser Stelle
extrahiert, dann erscheint ein anderes Ergebnis. Der Grund hierfür liegt in den
beschränkten Fähigkeiten des Präprozessors, der lediglich eine Textersetzung
durchführt. Um solchen Fehlern vorzubeugen ist es ratsam, so viel wie möglich zu
Klammern.
Mit #undef kann die Definition von Makros wieder aufgehoben werden.
70
5.1 Parser, Präprozessor
Es existieren mehrere vordefinierten Makros:
Makroname
Bedeutung
__LINE__
Zeilennummer in der momentanen Sourcecode-Datei
(Dezimalzahl).
Kann durch #line beeinflusst werden.
__FILE__
Dateiname des momentanen Sourcecodes (Zeichenkette).
Kann durch #line beeinflusst werden.
__DATE__
Datum der Übersetzung (Zeichenkette)
__TIME__
Zeitpunkt der Übersetzung (Zeichenkette)
__cplusplus
Momentan festgelegt auf die Zahl „199711L“. Dies wird
voraussichtlich in künftigen C++-Versionen geändert.
5.1.2.2
Dateien einfügen
Mit Hilfe der #include-Anweisung können andere Dateien in die gerade
bearbeitende Datei eingefügt werden. Dabei wird ein Dateiname angegeben:
#include <stdio.h>
#include "MyApp.h"
Steht der Dateiname in spitzen Klammern, dann wird die Datei in den StandardInclude-Verzeichnissen gesucht, die beispielsweise in Umgebungsvariablen des
Betriebssystems oder in speziellen Einstellungen des jeweiligen Compilers zu
finden sind.
Wird der Dateinamen in Hochkommas eingeschlossen, dann wird zuerst im
aktuellen Verzeichnis nachgesehen. Wird die gesuchte Datei nicht gefunden und
die gerade bearbeitende Datei ist auch nur eine Include-Datei in einer anderen
Datei, dann wird als nächstes im Verzeichnis dieser anderen Datei gesucht u.s.w..
Falls die Datei weiterhin nicht gefunden wurde, wird auch hier im StandardInclude-Verzeichnissen gesucht.
Dateinamen unterliegen der Makroersetzung.
71
5.1 Parser, Präprozessor
5.1.2.3
Bedingte Kompilierung
Durch die bedingte Kompilierung können ganze Code-Passagen ausgeblendet
werden. Dabei stehen mehrere Direktiven zur Auswahl:
Direktive
Bedeutung
#if
Verzweigt in Abhängigkeit von einem Ausdruck
#ifdef Verzweigt, wenn ein Makro mit dem angegebenen Namen existiert
#ifndef Verzweigt, wenn ein Makro mit dem angegebenen Namen nicht
existiert
#else
Alternative, wenn #if, #ifdef oder #ifndef nicht zutreffen
#elif
Alternative, wenn #if, #ifdef oder #ifndef nicht zutreffen in
Abhängigkeit von einem Ausdruck
#endif
Abschluß für #if, #ifdef oder #ifndef
Äußerst nützlich sind diese Anweisungen für Header-Dateien, in denen
üblicherweise die Klassen definiert werden. Diese Dateien werden oft mittels
#include in andere Dateien eingefügt. Werden sie mehrfach eingefügt – wie es
beispielsweise bei zyklischen Dateieinfügungen möglich ist – dann kann der
komplette Inhalt der Datei ausgeblendet werden.
Beispiel:
#ifndef _STDIO_H
#define _STDIO_H
...
#endif
5.1.2.4
Weitere Direktiven
Die Direktive #error gibt einen Fehler aus und beendet die Kompilierung. Mit
#pragma werden Compiler-spezifische Anweisungen gegeben. Durch #line
wird die im Compiler intern verwaltete Zeilennummer sowie der Dateiname
geändert. Dies kann durch die vordefinierten Makros „__LINE__“ und
„__FILE__“ abgefragt werden.
72
5.1 Parser, Präprozessor
5.1.2.5
Unterstützung durch das Programm
Ein Programm, das C++-Code verarbeitet, sollte auf jeden Fall mit PräprozessorAnweisungen umgehen können. Hier tauchen jedoch auch einige Probleme auf. Ist
beispielsweise der Name einer Klasse durch ein Makro entstanden und dieser
Name soll geändert werden, dann gibt es zwei Möglichkeiten: man kann das
Makro verändern oder man kann das Makro expandieren und in der expandierten
Zeichenkette den Namen ändern. Im ersten Fall müssen weitere Abhängigkeiten
berücksichtigt werden. Wird das Makro auch an anderen Positionen im
Sourcecode verwendet, dann würden diese Stellen ebenfalls verändert, was zu
unbeabsichtigten Resultaten führen kann. Wird hingegen der Text, der durch
Makroexpansion entstanden ist, modifiziert, kann es zu Problemen kommen,
wenn mehrere Makros an dieser Expansion beteiligt sind, oder wenn das Makro
noch andere Anweisungen enthält, die dann nicht mehr zur Verfügung stehen.
Beispiel:
#define HEAD(end) MyClass ## end : public Base
class HEAD(1) {..};
class HEAD(2) {..};
Das Ergebnis wäre:
class MyClass1 : public Base {..};
class MyClass2 : public Base {..};
Nach dem Ändern des Namens der ersten Klasse von „MyClass1“ in
„YourClass1“ im jeweiligen Fall und zurückschreiben als Sourcecode sieht das
Ergebnis folgendermaßen aus:
Fall 1: Makro verändern:
=> #define HEAD(end) YourClass ## end : public Base
class HEAD(1) {..};
class HEAD(2) {..};
73
5.1 Parser, Präprozessor
Mit dem Ergebnis:
class YourClass1 : public Base {..};
class YourClass2 : public Base {..};
Fall 2: Expandierten Text verändern:
#define HEAD(end) MyClass ## end : public Base
=> class YourClass1 : public Base {..};
class HEAD(2) {..};
Mit dem Ergebnis:
class YourClass1 : public Base {..};
class MyClass2
: public Base {..};
In diesem Fall stehen die übrigen Anweisungen im Makro (im Beispiel die
Angabe der Basisklassen) für die umbenannte Klasse nicht mehr zur Verfügung.
Auch wenn eine solche Verwendung von Präprozessor-Direktiven vermieden
werden sollte, ist sie dennoch möglich und muss berücksichtigt werden. Da ein
Programm eine solche Entscheidung nicht adäquat treffen kann, sollte in diesen
Fällen der Benutzer gefragt werden.
Auch die Verwendung von Makronamen in der (graphischen) Darstellung sollte
man überdenken. Natürlich könnten alle Makros expandiert und nur der
expandierte Text dargestellt werden. Oft sind Makronamen jedoch eine sinnvolle
Umschreibung nichts sagender Zahlenwerte. Ein Beispiel dafür sind die ID's der
Ressourcen eines Windows-Programms. Ist ein Dialog als Ressource erstellt
worden, kann man auf ihn zugreifen durch die Angabe seiner IdentifikationsNummer (ID). Nun ist die Angabe eines Präprozessor-Makronamens
„IDD_ABOUTBOX“ sicherlich aussagekräftiger als die simple Zahl „100“ (beim
Erstellen solcher Ressourcen beispielsweise mit Visual C++ werden solche
Makros automatisch angelegt). In solch speziellen Fällen kann man auf die
Makroexpansion verzichten und das Makro wie die Definition einer globalen
Variable betrachten. Zusätzliche Farben, Formatierungen oder Icons könnten die
Herkunft dieses Namens kenntlich machen.
74
5.1 Parser, Präprozessor
Unter [W3-CRefact] läuft zur Zeit ein Projekt zur Erlangung des akademischen
Grades Ph.D. Dort werden Möglichkeiten zur Refaktorisierung von C-Code
untersucht durch die Einbeziehung von Präprozessor-Direktiven in den
Ableitungsbaum.
5.1.3
Analyseverfahren
Zur Erstellung eines Ableitungsbaumes gibt es mehrere Verfahren. Nähere
Informationen hierzu sind beispielsweise in [HMU02] zu finden.
Mögliche Verfahren sind u.a.:
•
Younger
Der Cocke-Younger-Kasami-Algorithmus (CYK) verarbeitet beliebige
kontextfreie Sprachen. Die Grammatik muss allerdings ε-frei und in ChomskyNormalform sein. Dabei wird eine quadratische Tabelle aufgebaut, an dessen
unterem Ende das Wort ist. Zu jedem Hilfszeichen, das in einer Zelle der
Tabelle steht, gilt, dass dieses Hilfszeichen das Teilwort unter der Zelle
ableiten kann. Je weiter oben in der Tabelle ein Hilfszeichen steht, desto größer
ist das ableitbare Teilwort. Steht ganz oben das Startsymbol, dann kann dieses
das ganze Wort ableiten. Der Zeitbedarf ist O(n3).
•
Earley
Mit dem Earley-Verfahren können beliebige kontextfreie Grammatiken geparst
werden. Der Zeitbedarf ist O(n3). Dabei werden sukzessiv sogenannte
Itemlisten erstellt, die jeweils den Anfang der Eingabe korrekt erkennen.
•
Valiant
Dieses Verfahren nutzt ε-freie Grammatiken in Chomsky-Normalform. Valiant
reduziert die Berechnung auf die Multiplikation boolescher Matrizen. Der
Zeitbedarf dafür liegt momentan bei etwa O(n2,5). Die berechneten Matrizen
entsprechen im Wesentlichen der Tabelle des Younger-Verfahrens.
•
LL(k)-Parser
Ein Top-Down-Verfahren, bei dem k Zeichen vorausgesehen wird
(Lookahead), um die Entscheidung für die jeweilige Reduktion zu treffen. Der
Zeitbedarf ist linear, allerdings gilt das Verfahren nicht für beliebige
kontextfreie Sprachen und Grammatiken. Wenn LL(k) die Menge der durch
einen LL(k)-Parser erkennbaren Sprachen ist, dann gilt: LL(1) É .. É LL(k)
75
5.1 Parser, Präprozessor
•
LR(k)-Parser
Ein Bottom-Up-Verfahren, bei dem k Zeichen vorausgesehen wird, um die
Entscheidung für die jeweilige Reduktion zu treffen. Auch hier ist der
Zeitbedarf linear. Es können nicht alle kontextfreien Sprachen und
Grammatiken verarbeitet werden, allerdings ist die Menge der erkennbaren
Grammatiken größer als bei LL(k)-Verfahren.
Es gilt: LL(k) É LR(1) = LR(2) = .. = LR(k)
•
Rekursiv absteigender Parser
Bei diesem Verfahren wird für jedes Hilfszeichen eine Funktion erstellt. In
dieser Funktion werden nacheinander die verschiedenen rechten Seiten der
Regeln der Grammatik ausprobiert und dabei rekursiv andere Funktionen
aufgerufen. Somit terminiert das Verfahren nicht bei linksrekursiven
Hilfszeichen, denn dann würde eine Funktion sich immer wieder rekursiv
selbst aufrufen, ohne dass ein Zeichen von der Eingabe konsumiert wird. Der
Zeitbedarf ist im Allgemeinen exponentiell, aber das Verfahren ist einfach zu
implementieren.
Der Zeitbedarf der Verfahren, die beliebige kontextfreie Sprachen erkennen
können, ist zu groß, als dass sie sinnvoll eingesetzt werden könnten. Hier bietet
sich die Verwendung eines LR(k)-Parsers mit linearem Zeitbedarf an. Die
Grammatik für C++ ist im offiziellen Standard gegeben und mittels automatischer
Parsergeneratoren kann ein Parser generiert werden. Beliebte Generatoren sind
YACC (Yet Another Compiler Compiler) und dessen GNU-Version Bison
([W3-Bison]). Beide erstellen LALR(1)-Parser, wobei LALR eine Teilmenge von
LR ist. Benutzt man jedoch die Original-Grammatik, dann werden mehrere
Konflikte gemeldet.
Konflikte treten auf, wenn der Parser auch mit Lookahead keine eindeutige
Entscheidung treffen kann, wie er fortfahren soll. Es werden zwei Arten
unterschieden: shift/reduce und reduce/reduce-Konflikte.
Shift/reduce-Konflikte tauchen auf, wenn der Parser nicht weiß, ob er eine Regel
reduzieren oder die Eingabe weiterlesen soll. Ein bekanntes Beispiel dafür stellen
geschachtelte „if“-Anweisungen dar, wie sie schon in C verwendet wurden.
76
5.1 Parser, Präprozessor
Die Regeln der Grammatik sind:
1
2
3
4
5
6
statement: selection_statement | ..
selection_statement:
'if' '(' condition ')' statement
| 'if' '(' condition ')' statement 'else'
statement
Es wird folgendes Programm betrachtet:
if (..)
if (..)
..
else
..
Der Parser liest den Sourcecode Token für Token von links nach rechts. Das erste
Token ist das Schlüsselwort „if“. Dieser Präfix kommt in zwei unterschiedlichen
Regeln vor, die in den Zeilen (4) und (5) definiert sind. Da zu diesem Zeitpunkt
keine Unterscheidung zwischen diesen beiden Regeln getroffen werden kann, wird
der Präfix in einen Zwischenspeicher (Keller oder Stack) geschoben. Dies wird
shift genannt. Anschließend wird die Eingabe (der Sourcecode) weiter gelesen und
verarbeitet, bis der Parser am Schlüsselwort „else“ angekommen ist.
An dieser Stelle taucht der Konflikt auf, denn der Parser kann nicht entscheiden,
zu welcher der beiden „if“-Anweisungen das „else“ gehört. Es besteht also die
Möglichkeit, die zweite Regel aus Zeile (5) anzuwenden und weiterzulesen. Der
Parser kann allerdings auch mit der ersten Regel aus Zeile (4) eine Reduzierung
(reduce) durchzuführen.
Im ersten Fall (shift) würde das „else“ zu inneren (d.h. zu zweiten) und im zweiten
Fall (reduce) zur äußeren (d.h. zur ersten) „if“-Anweisungen gehören. Da keine
Unterscheidung zwischen shift und reduce getroffen werden kann, wird dieser
Konflikt shift/reduce-Konflikt genannt. Gelöst wird dies durch die
Parsergeneratoren meistens zugunsten von shift, was üblicherweise auch der
Sprache entspricht (in diesem Fall gehört das „else“ zur inneren „if“-Anweisung).
77
5.1 Parser, Präprozessor
Der andere Konflikt tritt auf, wenn zwei unterschiedliche Regeln zur Reduzierung
verwendet werden können. Beispiel:
01
02
03
04
05
06
07
08
09
10
11
12
using_directive:
'using' 'namespace' namespace_name ';'
namespace_name:
original_namespace_name
| namespace_alias
original_namespace_name:
identifier
namespace_alias:
identifier
Hat der Parser bereits die beiden Schlüsselwörter „using“ und namespace“
gelesen, dann folgt als nächstes ein identifier und anschließend ein Semikolon.
Der identifier kann jedoch mit zwei unterschiedlichen Regeln reduziert werden:
mit „original_namespace_name“ aus Zeile (8) und mit „namespace_alias“ aus
Zeile (11). Der Parser kann auch mit Lookahead – das in beiden Fällen das
Semikolon liefert – keine Entscheidung über die anzuwendende Regel treffen.
Dieser Konflikt wird reduce/reduce-Konflikt genannt. Die Parsergeneratoren
lösen diesen Konflikt, indem sie die textuell erste Regel anwenden (in diesem Fall
die Regel aus Zeile (8)).
Der Parsergenerator Bison meldet bei der C++-Grammatik aus dem offiziellen
Standard über 60 shift/reduce und über 700 reduce/reduce-Konflikte. Diese
müssten eigentlich einzeln per Hand gelöst werden.
Es stellt sich die Frage, ob es nicht bereits fertige Parser für diese Aufgabe gibt.
Tatsächlich existiert eine recht große Anzahl im Internet und diese können
teilweise auch frei heruntergeladen und genutzt werden. Allerdings wurde kein
Parser gefunden, der den Anforderungen entspricht, fehlerfrei arbeitet und alle
Konzepte der Programmiersprache unterstützt. Die Anforderung, die am
häufigsten fehlt, ist, dass der Eingabetext (also die Sourcecode-Datei) in Token
umgewandelt wird, ohne dass die Position der Token innerhalb der Datei
festgehalten wird. Will man also ein Token ändern – beispielsweise weil man eine
Variable umbenennen will – dann hat man keine Informationen über deren
78
5.1 Parser, Präprozessor
Position in der Datei. Eine reine Textersetzung aller Vorkommen des betreffenden
Namens kommt nicht in Frage, da ein solcher Name in einem anderen Kontext
unterschiedlich definiert sein könnte. Man könnte die Speicherung der Positionen
der einzelnen Token auch umgehen, indem man sämtliche Zeichen innerhalb des
Syntaxbaumes repräsentiert. Dies ist jedoch aufgrund des Präprozessors und der
üblicherweise recht früh stattfindenden Ausfilterung von sogenannten
Whitespaces (Leerzeichen, Kommentare,..) nicht möglich.
Das Endergebnis des Parsers muss also in einer Beziehung stehen zwischen den
fertig bearbeiteten Token und deren Position in der Ursprungsdatei. Die einfachste
Möglichkeit hierzu ist, in einer ersten Stufe den einzelnen Zeichen
Positionsangaben zuzuordnen. Diese Angaben werden dann während der
Ausführung des Präprozessors und des Parsers beibehalten.
Will man jedoch fertige Parser um diese Funktionalität erweitern, muss man
genau wissen, wo der Sourcecode des Parsers modifiziert werden muss. Da kein
Parser mit ausreichender Dokumentation gefunden wurde, kann dies in einer
regelrechten Sisyphus-Arbeit enden.
5.2
Automatisches Layout
Bei der Erstellung eines Klassendiagramms existieren Informationen über die
vorhandenen Elemente – beispielsweise durch Interaktion mit dem Benutzer –
aber die Positionen der Elemente innerhalb des Diagramms sind noch unbekannt.
Muss der Benutzer diese Elemente anordnen, nimmt dies unnötige Zeit in
Anspruch. Eine hilfreiche Funktion ist also das automatische Layouten, das in
diesem Abschnitt beschrieben werden soll.
Layoutalgorithmen basieren auf graphentheoretischen Verfahren. Zur
Vereinfachung und Verallgemeinerung ist die Definition eines Graphs beschränkt
auf eine Menge von Knoten und eine Menge von Kanten, wobei eine Kante (u,v)
die beiden Knoten u und v verbindet. Die Knoten entsprechen im konkreten Fall
den Klassen und Paketen, während die Kanten eine Repräsentation aller
Verbindungen zwischen Knoten sind (u.a. Assoziationen, Generalisierungen,
Aggregationen).
79
5.2 Automatisches Layout
5.2.1
Ästhetik
Ein Layoutalgorithmus sollte ein möglichst verständliches Diagramm erzeugen, so
dass Vererbungshierarchien und vernetzte Strukturen auf einen Blick erfasst
werden können. Dazu müssen bestimmte Ästhetikkriterien erfüllt werden. Dies
umfasst sowohl syntaktische als auch semantische Kriterien. Als syntaktische
Kriterien gelten solche, die nicht die Bedeutung der Elemente berücksichtigen,
beispielsweise die Anzahl der Kreuzungspunkte von Kanten. Im Gegensatz dazu
stehen die semantischen Kriterien wie die Darstellung der Vererbungshierarchie.
Als Ästhetikkriterien gelten:
•
Überschneidungen von Kanten
Sind zu viele Überschneidungen vorhanden, ist eine gute Lesbarkeit nicht
gewährleistet.
•
Abstand zwischen Knoten
Die Knoten sollten sich nicht überlappen und ihr Abstand sollte nicht zu groß
sein.
•
Länge der Kanten
Ist der Abstand zwischen zwei Knoten gering, so resultiert dies nicht
notwendigerweise in einer kurzen Verbindungslinie. Die Länge der
entsprechenden Kante kann groß werden, wenn sie einen anderen Knoten
umgehen muss, oder wenn die Anzahl der Kantenkreuzungen minimiert wird.
•
Abstand zwischen Kanten bzw. zwischen Knoten und Kanten
Zur besseren Unterscheidung sollte eine Kante nicht zu dicht an einer anderen
Kante oder einem Knoten sein.
•
Anzahl der Kantenknicke
Bei Kantenknicken kann der Linienverlauf nicht so gut erfasst werden. Dies ist
besonders wichtig bei orthogonalen Graphen, d.h. bei Graphen, in denen
einzelne Kantensegmente entweder horizontal oder vertikal verlaufen. Bei
Direktverbindungen und Kurven können keine Knicke auftreten.
•
Größe und Form des Diagramms
Die zur Verfügung stehende Zeichenfläche sollte berücksichtigt werden.
Kriterien hierfür sind die maximale Größe (zur Darstellung auf einem
bestimmten Ausgabemedium), das Verhältnis der Höhe zur Breite sowie eine
gleichmäßige Verteilung.
80
5.2 Automatisches Layout
•
Relative Position der Knoten
Es hat sich eingebürgert, bei Generalisierungen die allgemeinere Klasse über
der spezielleren Klasse zu platzieren.
•
Darstellung der Vererbungshierarchie
Oft wird bei mehreren abgeleiteten Klassen der shared target style (siehe
Abbildung 10) verwendet, um Klassenzusammenhänge besser zu visualisieren.
Dabei treffen sich die Kanten aller abgeleiteten Klassen an einem Punkt, von
dem aus eine Linie zur Basisklasse geht.
5.2.2
Layoutalgorithmen
Es existieren mehrere Ansätze für ein automatisches Layout, die sich in Effizienz
und Aufwand der Implementierung unterscheiden. Dabei werden verschiedene
Kriterien berücksichtigt.
Der Topology-Shape-Metrics-Ansatz
Dieser Ansatz dient der Darstellung orthogonaler Graphen. Es werden drei
grundlegende Eigenschaften unterschieden:
•
Topologie (Topology): Mit der Topologie wird die Anordnung der Elemente
beschrieben. Zwei Graphen haben dieselbe Topologie, wenn ein Graph durch
Umformung in den anderen überführt werden kann, ohne die Reihenfolge der
Kanten zu ändern, die einzelne Innenflächen (faces) umschließen.
•
Form (Shape): Zwei Graphen haben dieselbe Form, wenn sie dieselbe
Topologie besitzen und ein Graph durch Änderung der Kantenlängen in den
anderen überführt werden kann.
•
Metriken (Metrics): Zwei Graphen haben dieselben Metriken, wenn sie
dieselbe Topologie und Form besitzen und in Bezug auf Translation und
Rotation kongruent sind. Metriken beschreiben die Größe der Knoten und die
Länge der Kanten.
Das Verfahren zur Erstellung des Layouts baut auf diesen drei Eigenschaften auf:
Schritt 1: Planarisation (planarisation)
Es wird eine planarer Graphen gesucht, d.h. ein Graph ohne
Kantenkreuzungen. Kann eine Kantenkreuzung nicht vermieden
werden, wird ein neuer temporärer Knoten an der Kreuzung eingeführt
81
5.2 Automatisches Layout
und die Kanten entsprechend geändert. Dadurch wird die Topologie
festgelegt.
Schritt 2: Orthogonalisierung (orthogonalization)
Mit der Topologie wird die Form ermittelt. Als Ergebnis entsteht für
jede Kante eine Liste mit Kantenknicken.
Schritt 3: Kompression (compaction)
Es werden minimale Kantenlängen berechnet und somit das endgültige
Layout festgelegt. Die in Schritt 1 eingeführten temporären Knoten
werden wieder entfernt.
Der Hierarchische Ansatz
Hierbei wird auf hierarchische Strukturen – wie Vererbung – Rücksicht
genommen durch die Aufteilung in mehrere horizontale Ebenen. Als
Voraussetzung wird ein azyklischer gerichteter Graph angenommen. Das
Verfahren arbeitet in den folgenden drei Schritten:
Schritt 1: Ebenen-Zuweisung (layer assignment)
Jeder Knoten wird einer Ebene E1, E2, ... En zugewiesen, so dass für alle
Kanten (u,v) mit u Î Ei und v Î Ej gilt: i < j. Anschaulich besitzt jeder
Knoten einer Ebene dieselbe y-Koordinate.
Schritt 2: Kreuzungs-Reduktion (crossing reduction)
Die Reihenfolge der Knoten jeder Ebene wird bestimmt, so dass die
Anzahl der Kreuzungen minimiert ist.
Schritt 3: X-Koordinaten-Zuweisung (x-coordinate assignment)
Jedem Knoten wird eine x-Koordinate zugewiesen.
Die geforderte Voraussetzung eines azyklisch gerichteten Graphen ist in der
Praxis nicht immer vorhanden. Um zyklische gerichtete Graphen zu verarbeiten,
wird für eine möglichst kleine Menge von Kanten die Richtung geändert, so dass
der Graph azyklisch wird. Nach der Berechnung durch den Hierarchischen Ansatz
wird die ursprüngliche Richtung wiederhergestellt. Ist der Graph ungerichtet, wird
ähnlich verfahren und der Graph temporär in einen gerichteten transformiert.
82
5.2 Automatisches Layout
Der Visibility-Ansatz
In diesem Ansatz werden Kanten in Form von aufeinander folgenden
Liniensegmenten beliebiger Richtung und Länge dargestellt. Auch dieser Ansatz
ist in drei Schritte gegliedert:
Schritt 1: Planarisation (planarization)
Dieser Schritt entspricht dem ersten Schritt des Topology-ShapeMetrics-Ansatzes.
Schritt 2: Sichtbarkeit (visibility)
Jeder Knoten wird auf ein horizontales Segment (eine horizontale Linie
beschränkter Länge) und jede Kante auf ein vertikales Segment
abgebildet, so dass es keine Überschneidungen gibt.
Schritt 3: Ersetzung (Replacement)
Horizontale Segmente werden wieder durch Knoten und vertikale
Segmente durch Kanten ersetzt.
Der erste Schritt ist mit dem Topology-Shape-Metrics-Ansatz identisch. Der
zweite Schritt ähnelt der Ebenen-Zuweisung des Hierarchischen Ansatzes. Somit
kann dieses Verfahren als Kombination der beiden Ansätze aufgefasst werden.
Der Vergrößerungs-Ansatz
Hierbei werden neue Knoten und Kanten hinzugefügt und der Graph vergrößert,
so dass ein Graph entsteht, der geradlinig gezeichnet werden kann:
Schritt 1: Planarisation (planarization)
Dieser Schritt entspricht dem ersten Schritt des Topology-ShapeMetrics-Ansatzes.
Schritt 2: Vergrößerung (augmentation)
Es werden Knoten und Kanten hinzugefügt, bis ein maximal planarer
Graph entsteht. Dieser zeichnet sich dadurch aus, dass jede Innenfläche
von genau drei Kanten umschlossen wird.
Schritt 3: Dreiecks-Zeichnung (triangulation drawing)
Jede Fläche wird in ein Dreieck umgewandelt. Die in Schritt 2
hinzugefügten Knoten und Kanten werden wieder entfernt.
83
5.3 Das Programm DiaClassma
5.3
Das Programm DiaClassma
Das erstellte Programm trägt den Namen DiaClassma, da es Klassen in Form von
Diagrammen darstellt. Es soll im Folgenden vorgestellt werden.
5.3.1
Übersicht
DiaClassma besteht aus den in Abbildung 38 gezeigten Komponenten.
Die Graphische Benutzungsoberfläche besteht aus den Klassen zur Darstellung
von Fenstern mit Menüs sowie Ausgabefenstern und dem Klassendiagramm. Die
Verbindung zum Parser und zu den vom Parser erstellten Daten geschieht über
eine Klasse namens CDataDoc. Auf diese Weise können GUI oder
Parser/Datenverwaltung ausgetauscht werden, ohne im gesamten Programm
Verweise ändern zu müssen.
Es gibt zwei globale Klassen, d.h. Klassen, die im gesamten Programm verfügbar
sind und benutzt werden können: CGlobal und CLanguage. CGlobal bietet
eine einfache Funktion, um einzelne Textzeilen zu speichern und abzurufen und
ist gedacht für die Speicherung von Fehlermeldungen, die innerhalb bestimmter
Komponenten auftauchen und an anderer Stelle ausgegeben werden. Momentan
wird es benutzt, um Fehlermeldungen während des Parsens zu verwalten. In der
Klasse CLanguage werden alle sprachspezifischen Texte festgehalten, wobei
sich „Sprache“ hierbei auf natürliche Sprachen (deutsch, englisch, ..) bezieht.
GUI
CDataDoc
Parser
+
Ausgabe
Klassendiagramm
Datenverwaltung
Global
CGlobal
CLanguage
Abbildung 38: Die Komponenten von DiaClassma
84
5.3 Das Programm DiaClassma
Wann immer ein Text in einem Menü, einem Dialog o.ä. erscheint, wird er aus
dieser Klasse geholt. Auf diese Weise kann das Programm einfach um die
Unterstützung zusätzlicher Sprachen erweitert werden.
Zusätzlich zu diesen beiden Klassen gibt es zwei Enumeratoren, die im gesamten
Programm verwendet werden können. Dies ist zum Einen enAccSpec
(Kurzform für „enum Access Specifier“), das für die Spezifizierung der
Sichtbarkeit (public, private, protected) zuständig ist, und zum Anderen enItem,
das den Typ eines Datenelements darstellt (u.a. Methode, Klasse, Datei,
Enumerator).
5.3.2
Externe Komponenten
Es wurden zwei Komponenten von
CSizingControlBar und CMemDC.
anderen
Autoren
verwendet:
CSizingControlBar ist ein kleines Fenster, das sich an den Rändern des
Programmfensters andocken oder auch als eigenständiges Fenster existieren kann.
Es hat eine Titelleiste, kann in der Größe verändert werden und
kann ein beliebiges Fenster in seiner Mitte darstellen. In
Diaclassma wird es benutzt zur Darstellung der Baumstruktur, in
der die Klassen und Diagramme verwaltet werden, sowie für ein
Ausgabefenster. CSizingControlBar kann unter [W3-SiCoBa]
gefunden werden.
CMemDC erstellt anhand eines gegebenen Gerätekontextes einen kompatiblen
Kontext im Speicher. Ein Gerätekontext ist eine Datenstruktur in Windows, die
Informationen über das Zeichen mit einem bestimmten Gerät (z.B. Monitor,
Drucker..) enthält. Will man etwas auf dem Bildschirm ausgeben, dann geschieht
dies mit Hilfe eines solchen Gerätekontextes. Beim Zeichnen eines Bildes wird
zuerst das komplette Bild mit der Hintergrundfarbe überschrieben und
anschließend komplett neu erstellt. Wird dies direkt auf dem Bildschirm
ausgegeben, dann kommt es zum Flimmern. Benutzt man CMemDC, dann kann das
Bild zuerst vollständig im Speicher vorberechnet und gezeichnet werden und wird
erst danach auf dem Bildschirm ausgegeben. CMemDC wurde für Diaclassma um
die Unterstützung von Zeichensätzen erweitert. Es wird unter [W3-CodePro] zum
Download angeboten.
85
5.3 Das Programm DiaClassma
5.3.3
Graphische Benutzungsoberfläche
Die GUI ist in drei Bereiche unterteilt. Im unteren Bereich ist ein Ausgabefenster,
in dem jederzeit Meldungen ausgegeben werden können. Durch die Verwendung
der Komponente CSizingControlBar ist dieses Fenster nicht an die
vorgegebene Position gebunden. Am linken Rand befindet sich der Explorer. Dies
ist ein Fenster, in dem die Elemente des Projekts in einer Baumstruktur verwaltet
werden. Der restliche Bereich ist für die Diagramme vorgesehen. Zur Zeit können
nur Klassendiagramme erstellt werden.
Über den Menüpunkt Datei – Öffnen können neue Sourcecode-Dateien
hinzugefügt und mit Diagramm – Neues Klassendiagramm ein neues
Klassendiagramm erzeugt werden. Entsprechende Symbole sind in der Toolbar
vorhanden. Dort sind ebenfalls drei Symbole für eine Zoom-Funktion
untergebracht, die alle die Form einer Lupe haben. Das erste Symbol – mit einem
„+“ in der Mitte – dient dem Vergrößern. Mit dem zweiten Symbol – in dessen
Mitte ein „-“ angezeigt wird – wird das Diagramm verkleinert. Das letzte Symbol
stellt die Originalgröße wieder her.
Abbildung 39: Screenshot DiaClassma
86
5.3 Das Programm DiaClassma
5.3.3.1
Explorer
Der Explorer ist für die Verwaltung sämtlicher Elemente des Programms
zuständig. Dies sind die per Reverse Engineering gefundenen oder in DiaClassma
angelegten Sprachelemente wie Klassen, Methoden u.a. sowie die zugehörigen
Sourcecode-Dateien und Diagramme, die alle in einer Baumstruktur dargestellt
werden. Durch die Verwendung der Komponente CSizingControlBar kann
der Explorer sich an den Rand des Programms andocken oder als frei
schwebendes Fenster fungieren. Jedes Element der Baumstruktur wird durch ein
vorangestelltes Icon visualisiert. Diese Icons sind im Folgenden aufgelistet.
Sprachelemente:
public
protected
private
Klasse
Attribut
Methode
Konstruktor
Destruktor
Enumerator
Namespace
Verwaltungselemente:
Implementierungssicht: Alle Elemente, die direkt mit dem Sourcecode
abgeglichen werden.
Designsicht: Elemente, die keinen direkten Bezug auf den Sourcecode
haben.
Sprachelemente entsprechend obiger Tabelle (Klassen, Methoden, ...)
Dateien
Diagramme
Einzelne Datei mit Dateinamen
Klassendiagramm
87
5.3 Das Programm DiaClassma
5.3.3.2
Klassendiagramm
Das Klassendiagramm wird über das Kontextmenü gesteuert. Durch den ersten
Punkt „Angezeigte Klassen auswählen“ öffnet sich ein Dialogfenster, in dem alle
Klassen dargestellt werden. Die Ansicht entspricht der des Explorers, allerdings
werden nur Klassen und Namensräume ohne Methoden und Attribute dargestellt.
Hier können die Klassen ausgewählt werden, die in dem Diagramm angezeigt
werden sollen.
Die nächsten vier Punkte sind nur verfügbar,
wenn im Diagramm mindestens eine Klasse
selektiert wurde. Die Funktionalität gilt dann für
alle selektierten Klassen. Mit „Farbe ändern“
kann eine individuelle Farbe zugewiesen werden.
Der Punkt „Layout“ ist untergliedert in die
Funktionen „horizontal ausrichten“, „horizontal
gleichmäßig verteilen“, „vertikal ausrichten“ und
Abbildung 40: Das Kontextmenü
„vertikal gleichmäßig verteilen“. Bei der ersten
im Klassendiagramm von
Funktion wird von allen selektierten Klassen die
DiaClassma
minimale Position in vertikaler Richtung
ermittelt und alle Klassen auf diese Position gesetzt. Die Elemente werden also an
einer horizontalen Linie ausgerichtet. Durch die Funktion „horizontal gleichmäßig
Verteilen“ werden die Abstände zwischen den selektierten Klassen in horizontaler
Richtung gleichgesetzt. Entsprechendes gilt für die vertikale Richtung. Die
Menüpunkte
„Attribute“
und
„Methoden“
enthalten
jeweils
die
Auswahlmöglichkeiten „Anzeigen“, „Verbergen“ und Sortieren“ und sind
selbsterklärend.
Der anschließende Menüpunkt „Exportieren“ speichert das Diagramm als
Graphik-Datei. Dabei wird die Programmierschnittstelle GDI+ (graphics design
interface) von Windows benutzt, um Dateien in unterschiedlichen Formaten zu
speichern (beispielsweise .bmp, .jpg oder .png).
Mit dem letzten Menüpunkt „Eigenschaften“ lassen sich die Größe des
Diagramms und eine Bezeichnung festlegen.
5.3.4
Parser
Der Parser ist für das Reverse Engineering zuständig. Alle diesbezüglichen
Klassen haben den Präfix „CCPP“, wobei das erste Zeichen für „Class“ steht und
88
5.3 Das Programm DiaClassma
die nächsten drei Zeichen für die zu analysierende Programmiersprache C++.Der
erste Schritt des Parsens ist das Einlesen einer Sourcecode-Datei, das Erstellen
von Token und das Ausführen von Präprozessor-Direktiven. Dafür ist die Klasse
Abbildung 41: Der Parser in DiaClassma, erstellt mit DiaClassma
CCPPLexer zuständig. Nach dem Lesen der Datei werden Trigraph-Sequenzen
ersetzt, Zeilen zusammengefügt und Token erstellt. Dabei bedient sich der Lexer
der Klasse CCPPToken, die Informationen über die verschiedenen Arten von
Token, Schlüsselwörter sowie Punktuatoren und Operatoren enthält. Jedes so
entstandene Token wird durch ein Objekt der Struktur stCPPToken gespeichert
und anschließend durch den Präprozessor verarbeitet. Zur Definition von Makros
sowie zur Makroersetzung zeichnet sich die Klasse CCPPMacro verantwortlich,
die eng mit CCPPLexer verzahnt ist. Jedes Makro wird durch die nested Struktur
stCPPMakro repräsentiert, die Informationen über Namen, Parameter und
Ersatztext enthält. Eine boolesche Variable gibt an, ob das Makro noch aktiv ist.
Dies wird benötigt für den Fall, dass es durch eine #undef-Direktive ungültig
wurde. Für die Verarbeitung von #include-Direktiven wurde ein spezielles
Token eingeführt, über das auf ein weiteres
Objekt der Klasse CCPPLexer zugegriffen
werden kann. Wird eine entsprechende Direktive
gefunden, dann wird rekursiv ein solches Objekt
erstellt und in die Liste der Token eingefügt. Es
entsteht eine Baumstruktur, in der jeder Knoten
Abbildung 42: Baumstruktur des
Lexers
entweder ein Token der Sprache oder ein
weiteres CCPPLexer-Objekt darstellt (siehe Abbildung 42).
Nach dem Erstellen der Token und dem Ausführen des Präprozessors wird der
89
5.3 Das Programm DiaClassma
Code auf Konstruktionselemente hin untersucht und die gefundenen Elemente
einer verwaltenden Datenstruktur hinzugefügt. Dies wird durch die Klasse
CCPPAnalyzer realisiert, die die Token von CCPPLexer erhält. Da die Token
der Form stCPPToken, die in CCPPLexer gespeichert sind, für die Analyse
nicht ausreichen, wurde ein erweitertes Token erstellt. stCPPTokenEx
aggregiert stCPPToken und enthält zusätzlich den Namen der Datei, aus der das
Token stammt.
Bei der Analyse wird von Lookaheads variabler Länge Gebrauch gemacht, d.h.
CCPPAnalyzer liest voraus, bis eine Entscheidung getroffen werden kann,
welches Konstrukt vorliegt. Aufgrund der Schwierigkeiten mit der Grammatik des
C++-Standards wurde auf das Aufbauen eines Ableitungsbaumes verzichtet und
direkt nach den Sprachelementen Methode, Attribut, Klasse u.a. gesucht.
5.3.5
Datenverwaltung
Durch das Reverse Engineering erhält man die Konstruktionselemente, also u.a.
Klassen, Methoden, Attribute. Die Verwaltung dieser Elemente wird durch die
Datenstruktur realisiert, die in Abbildung 43 aufgezeigt wird. Sie besteht aus
mehreren Klassen, deren Namen alle mit CData beginnen. Zusätzliche nested
Strukturen (stFilePos, stPosAndRef und stClassAndAccess) sind zur
Speicherung einzelner Informationen konzipiert und enthalten keine Methoden,
sondern lediglich einige Attribute. Die Strukturen haben keine öffentliche
Sichtbarkeit; der Zugriff auf sie ist durch Methoden gekapselt.
Alle Konstruktionselemente werden in Klassen gespeichert, die von CDataItem
abgeleitet sind. CDataItem enthält grundlegende Informationen zur
Beschreibung des Typs des Items, zur Speicherung des Namens und einer
eindeutigen Identifikationsnummer (ID). Weiterhin werden Referenzen verwaltet,
die alle Positionen im Sourcecode angeben, an denen der Name dieses Items
auftaucht.
Die Gemeinsamkeiten von Variablen bzw. Attributen und Funktionen bzw.
Methoden werden durch die Klasse CDataVariableOrMethod realisiert. Dies
betrifft den Datentyp im Fall einer Variablen bzw. eines Attributs und den
Datentyp des Rückgabewertes im Fall einer Funktion bzw. einer Methode. Ist der
Datentyp eine Klasse, dann wird auch ein Verweis auf die entsprechende Klasse
gesetzt. Variablen und Attribute werden durch die Klasse CDataVariable
verwaltet, die von CDataVariableOrMethod abgeleitet ist und dessen
90
5.3 Das Programm DiaClassma
Funktionalität um einen Initialwert sowie Informationen über Aggregation und
Komposition erweitert. Für Funktionen und Methoden ist die Klasse
CDataMethod
zuständig,
die
die
Funktionalität
von
CDataVariableOrMethod um Funktionsparameter erweitert, die in Form
einer doppelt verketteten Liste von Variablen gespeichert werden.
Abbildung 43: Die Datenverwaltung in DiaClassma, erstellt mit DiaClassma
Analog zu dieser Vorgehensweise werden die Gemeinsamkeiten von Klassen und
Namespaces durch eine separate Klasse realisiert: CDataContainer. Hier
werden Informationen über enthaltene Enumeratoren, Variablen, Methoden und
enthaltene Container sowie eine Liste von Alias-Namen verwaltet. Die definierten
Subklassen sind CDataClass und CDataNamespace. Letzteres erweitert die
Funktionalität um die Benutzung anderer Namespaces, die in C++ durch die
using-Direktive mit dem Schlüsselwort using festgelegt werden können.
CDataClass erweitert die Funktionalität um Informationen über Basisklassen
und abgeleitete Klassen.
In CDataFile werden Dateien und in CDataEnum Enumeratoren gespeichert.
Sämtliche Daten werden in CData gespeichert. Diese Klasse enthält eine Liste
mit Dateien sowie einen Zeiger auf einen globalen Container, der alle anderen
Sprachelemente enthält. Weiterhin werden in CData IDs verwaltet. Durch die
Verwendung von statischen Variablen wird keine ID doppelt vergeben.
91
Kapitel 6 Bewertung
Es gibt viele Möglichkeiten zur graphischen Darstellung von Sourcecode; als
Standard hat sich inzwischen UML etabliert. Einige der Diagramme dieser
Notation lassen sich jedoch nicht ohne weitere Informationen des Benutzers in
Sourcecode abbilden oder aus vorhandenem Sourcecode gewinnen. Manche
Modellelemente können überhaupt nicht in Code übersetzt werden. Da
unterschiedliche Modellelemente in verschiedenen Diagrammenarten vorkommen
können, ist auch die Übersetzung dieser Diagramme problematisch.
Dies wird auch in der Analyse zum Stand der Technik deutlich. Die getesteten
Systeme sind für die Analyse einer konkreten Aufgabenstellung, das Design von
neuem Sourcecode sowie für die Erstellung von informativen Graphiken zur
nachträglichen Dokumentation konzipiert worden, und hier werden auch ihre
Stärken deutlich. Bei der Unterstützung der eigentlichen Programmierung, also
der Visualisierung von Sourcecode und der vernetzten Strukturen der
Sprachelemente während der Programmierung, offenbaren sich bei den getesteten
Programmen einige Mängel.
Oft wird ein plattformunabhängiges Modell (PIM: plattform independant model)
erstellt und plattformabhängige Informationen – wie das Layout des Sourcecodes
– vernachlässigt. Das Resultat ist ein veränderter Code, auch wenn im Modell
keine oder nur minimale Änderungen vorgenommen wurden. Zusätzlich
eingefügte Kommentare und Veränderungen in der Struktur der SourcecodeDateien (Klassen werden in separate Dateien ausgelagert, Einrückungen
verschwinden) verfremden das Aussehen des Codes und erschweren ein
weiterarbeiten. Ein solches Ergebnis ist jedoch nicht wünschenswert.
Sinnvoller ist hier eine Erweiterung von Texteditoren um graphische Elemente.
Parallel zur Bearbeitung des Sourcecodes in Textform sollte die graphische
Darstellung der Sprachelemente berechnet werden, die auf Wunsch des Benutzers
wahlweise angezeigt oder ausgeblendet werden kann. So ist sowohl die
Bearbeitung des Textes als auch die Bearbeitung der Graphik möglich und die
Konsistenz bleibt gewährleistet.
Zur graphischen Darstellung können einige Elemente von UML benutzt werden,
auch wenn sie nicht für diesen Zweck gedacht sind. Kleinerer Modifikationen –
92
Kapitel 6 Bewertung
wie die Benutzung von Symbolen anstatt Textzeichen für die Sichtbarkeit –
erleichtern das visuelle Erfassen von Sachverhalten.
Die prototypische Implementierung in Form des Programms DiaClassma ist
aufgrund der beschriebenen Probleme bezüglich der Zielsprache C++ leider nicht
sehr weit fortgeschritten, weshalb das erarbeitete Konzept nicht im Praxiseinsatz
getestet werden konnte. Auch ein Vergleich zu den im Kapitel 3 Stand der
Technik beschriebenen Programmen sollte nicht gezogen werden aufgrund des
frühen Entwicklungsstadiums von DiaClassma, und weil das Programm mehrfach
mit dem Sourcecode getestet wurde, der auch bei der Untersuchung der Merkmale
der Testprogramme zum Einsatz kam.
93
94
Anhang A Glossar
Anhang A Glossar
Abstrakt
Eine Methode ist abstrakt, wenn sie nur einen Methodenrumpf ohne
Implementierung hat. Eine Klasse ist abstrakt, wenn sie eine abstrakte Methode
hat. Eine solche Klasse kann nicht instanziert werden.
Aggregation
In der objektorientierten Programmierung ist die Aggregation eine spezielle
Beziehung zwischen zwei Klassen, wobei eine Klasse ein Teil einer anderen
Klasse ist. Beispiel: Klasse Auto aggregiert die Klasse Reifen.
siehe Komposition
Basisklasse
siehe Superklasse
CASE
Computer Aided Software Engineering
CASE bezeichnet die Computer seitige Unterstützung während der SoftwareEntwicklung.
Compiler
Ein Programm zur Übersetzung eines Quellcodes einer Sprache in eine
Zielsprache ohne die Semantik zu verändern. Oft ist die Zielsprache
Maschinensprache.
Design Pattern
Ein Muster bzw. eine Schablone für ein Design.
Einfachvererbung
Kann in einer objektorientierten Programmiersprache eine Klasse von nur einer
anderen Klasse erben, so spricht man von Einfachvererbung.
siehe Mehrfachvererbung, Vererbung
Forward Engineering
Durch das Forward Engineering wird aus Klassen mit Attributen und Methoden
Sourcecode erzeugt.
siehe Reverse Engineering, Roundtrip Engineering
95
Anhang A Glossar
Generalisierung
Eine Beziehung von einem spezielleren Objekt zu einem allgemeineren Objekt. In
der objektorientierten Programmierung besteht eine Generalisierung von einer
Subklasse zu seiner Superklasse. Die entgegengesetzte Richtung wird
Spezialisierung genannt.
siehe Subklasse, Superklasse
GUI
Graphical User Interface (Graphische Benutzungsoberfläche)
Eine graphische Schnittstelle, durch die ein Benutzer mit dem Computer
kommunizieren kann. GUIs werden meist mit Fenstern, Menüs und graphischen
Symbolen realisiert. Einen weit verbreiteter Typ von GUIs bezeichnet man als
WIMP-Interface (Windows, Icons, Mouse, and Pointer).
Intrinsische Datentypen
siehe primitive Datentypen
Kapselung
Ein Zugriff auf die interne Struktur ist nur über klar definierte Schnittstellen
möglich. So kann es beispielsweise setter- bzw. getter-Methoden für ein Attribut
einer Klasse geben, so dass das Setzen eines Attributs bzw. die Rückgabe des
Wertes eines Attributs nur über diese Methoden erlaubt ist.
Komposition
Die Komposition stellt eine besondere Form der Aggregation dar. Dabei kann das
Teil nicht ohne das Ganze existieren. Beispiel: die Klassen Haus und Zimmer.
siehe Aggregation
MDA
Model Driven Architecture
Standard der OMG zur plattformneutralen Erstellung von Software-Systemen.
siehe OMG, UML
Mehrfachvererbung
Kann in einer objektorientierten Programmiersprache eine Klasse von mehreren
anderen Klassen erben, so spricht man von Mehrfachvererbung.
siehe Einfachvererbung, Vererbung
96
Anhang A Glossar
MOF
Meta-Object Facility
Standard der OMG, die das MOF-Modell definiert. Dies ist ein Meta-Metamodell,
das eine gemeinsame Grundlage verschiedener Metamodelle beschreibt.
siehe OMG, 2.2.2.1 Die Four Layer Modeling Architecture
Multiplizität
Bei einer Beziehung zwischen zwei Dingen gibt die Multiplizität (Vielfachheit)
an, wie viele Elemente der einen Sorte mit wie vielen Elementen der anderen
Sorte von Dingen in Beziehung stehen.
Nested Klassen
Wird eine Klasse innerhalb einer anderen Klasse definiert, dann nennt man diese
„nested“ (engl. für eingenistet).
OCL
Object Constraint Language
OCL wird in UML benutzt, um Einschränkungen und Zusicherungen zu
definieren. OCL ist ein Standard der OMG.
siehe UML, OMG
OMG
Object Management Group
Non-for-Profit Konsortium, das u.a. UML standardisiert.
siehe UML, MDA, XMI, OCL
OOPSLA
Object-Oriented Programming, Systems, Languages and Applications
Eine von der amerikanischen Informatiker-Vereinigung ACM jährlich
durchgeführte Konferenz zum Thema Objekt-Orientierung und SoftwareEngineering.
Polymorphie
Von Polymorphie spricht man, wenn verschiedene Klassen auf die gleiche
Nachricht unterschiedlich reagieren. Das Verhalten ist dann nicht vorhersagbar,
sondern polymorph (vielgestaltig). Dies ist der Fall, wenn eine Klasse eine
Methode definiert, die von mehreren abgeleiteten Klassen überschrieben und um
spezifische Funktionalität erweitert wird.
97
Anhang A Glossar
primitive Datentypen
Datentypen einer Programmiersprache, die ohne vorherige Definition benutzt
werden können (z.B. int, char, float). Andere Bezeichnung: intrinsische
Datentypen
Punktuator
Punktuatoren sind Zeichen einer Programmiersprache, die keinen Wert darstellen,
aber eine syntaktische und semantische Bedeutung haben. In C++ sind dies u.a.
die folgenden Zeichen: { } [ ] ^ + ; , .
Refaktorisierung
Bei der Refaktorisierung (engl.: Refactoring) wird der Quellcode einer Software
umstrukturiert, ohne seine Semantik und Funktionalität zu verändern.
Reverse Engineering
Durch das Reverse Engineering (engl. für umgekehrt entwickeln, rekonstruieren)
werden aus einem fertigen System (hier: ein Sourcecode in Textform) die
Konstruktionselemente wie Klassen mit Attributen und Methoden gefunden.
siehe Forward Engineering, Roundtrip Engineering
Roundtrip Engineering
Ist eine Kombination aus Reverse und Forward Engineering, bei der aus
Sourcecode die Konstruktionselemente (Klassen, Methoden u.a.) gefunden und
evtl. verändert werden, um dann wieder in Sourcecode umgewandelt zu werden.
Wird nichts an den Konstruktionselementen verändert, dann sollte im Idealfall der
ursprüngliche Code wieder entstehen.
siehe Forward Engineering, Reverse Engineering
Schnittstelle
Eine Schnittstelle (engl: Interface) ist ein Teil eines Systems zum Austausch von
Informationen. In einer Programmiersprache ist eine Schnittstelle eine Klasse, die
nur abstrakte Methoden hat. Eine abgeleitete Klasse muss diese Methoden um
Funktionalität erweitern.
siehe abstrakt
Spezialisierung
siehe Generalisierung
98
Anhang A Glossar
Subklasse
Bei der Vererbung der objektorientierten Programmierung erbt die Subklasse alle
Attribute und Methoden von der Superklasse. Auch Unterklasse oder abgeleitete
Klasse genannt.
siehe Superklasse
Superklasse
Bei der Vererbung der objektorientierten Programmierung erbt die Subklasse alle
Attribute und Methoden von der Superklasse. Auch Oberklasse oder Basisklasse
genannt.
siehe Subklasse
Token
Die kleinste sinngebende Einheit einer Programmiersprache.
UML
Unified Modeling Language
UML ist eine graphische Sprache zur Visualisierung, Spezifizierung, Konstruktion
und Dokumentation von Software-Systemen. Standardisiert von OMG.
siehe OMG, UML, MDA, XMI, OCL
Vererbung
Ein Konzept aus der objektorientierten Programmierung. Dabei „erbt“ eine Klasse
alle Attribute und Methoden einer anderen Klasse.
Whitespace
Die Zeichen eines Textes, die im Texteditor nicht sichtbar sind. Dies sind u.a.
Leerzeichen, Tabulator und das Zeilenumbruchzeichen. In vielen
Programmiersprachen werden sie in einer recht frühen Phase der Übersetzung
durch den Compiler ausgeblendet. In der Whitespace-Programmiersprache
hingegen besteht der Quellcode ausschließlich aus Whitespace-Zeichen.
XMI
XML Metadata Interchange
XMI beschreibt, wie Instanzen der Elemente des MOF-Modells auf XML
Definitionen abgebildet werden können. Somit ist ein Austausch von UMLModellen möglich.
siehe OMG, UML
99
Anhang B Stand der Technik
Anhang B Stand der Technik
Informationen
Titel
Hersteller
Homepage
Sprachen
Testversion Preis
ClassBuilder 2.4 Alpha 1.7
http://sourceforge.net/
C++
Vollversion 0
Enterprise Architect 4.0 (729) C++, Java, C#, 30 Tage
Sparx Systems
VB.Net,
www.sparxsystems.com.au
Delphi, PHP
125 $ - 225 $
Jumli 1.4
Schwäbisch Hall AG
www.jumli.de/
C++, Java, C#
Vollversion 0
Metamill v3.1 (build 556)
Metamill
www.metamill.com
C++, Java,
ANSI C, C#
30 Tage,
max. 20
Elemente
pro
Diagramm
125 $ Windows
75 $ Linux
Max. 30
Klassen
995 $ 1245 $
ObjectDomain R3 (build 292) C++, Java,
ObjectDomain
Python, IDL
www.objectdomain.com
ObjectiF 4.7 (Enterprise)
microTOOL
www.microtool.de
C++, Java, VB Vollversion 0 3828 €
Rational Rose Enterprise
Edition 2003
IBM
www.ibm.com
C++, Java, VB, 14 Tage
Ada83, Ada95,
CORBA,
Oracle8
Select Component Factory 5.0 C++, Java, VB, Max. 1
Select Business Solutions
C#, SQL, „and Diagramm
www.selectbs.com
many other“
pro Typ
1795 $
3787 € Download
3884 € Post
100
Anhang B Stand der Technik
Titel
Hersteller
Homepage
Sprachen
Testversion Preis
UMLStudio 7.1 (Build 746)
PragSoft
www.pragsoft.com
Java, C++,
CORBA IDL,
Ada95
(nur Forward:
Forte TOOL,
Maxim)
„cannot
save
large
projects“
WithClass 2000 Enterprise 6.0 C++, Java,
30 Tage
MicroGold
Delphi, VB,
www.microgold.com
IDL, Perl, PHP,
C#
500 $ Commercial
250 $ Private,
125 $ Educational
600 $ für
Studenten,
800 $ sonst
Merkmale
Bemerkungen:
ü vorhanden
m nicht vorhanden
Hierarchie: Bietet das Programm ein automatisches Layout an, dann zeigt diese
Spalte, ob die Vererbungsbeziehung zwischen Klassen in hierarchischer Form
(ü), oder ob sie wie jede andere Beziehung (m) dargestellt werden.
Datei pro Klasse: Gibt an, ob beim Forward Engineering für jede
Klasse/Struktur/Union eine separate Datei angelegt wird.
Vererbung, Aggregation, Komposition: Zeigt die Darstellung dieser drei Konzepte
der objektorientierten Programmierung nach dem Reverse Engineering oder „m“,
falls die entsprechende Beziehung nicht aus vorhandenem Sourcecode gewonnen
werden konnte.
101
Komposition
Aggregation
Vererbung
Roundtrip
Datei pro Klasse
Forward
ü m ü
ü
ü ü ü
ü
ü ü ü
Enterprise
Architect
ü ü m
ü
ü m ü
ü
m m m
Jumli
ü ü m
m
ü m ü
ü
ü m m
m
Metamill
ü ü m
m
ü m ü
ü
m m m
m m
m
ü
ü ü ü
ü
m m ü
ObjectiF
ü m m
m
ü m ü
ü
ü m m
Rational Rose
ü m m
ü
ü ü ü
ü
ü m m
Select Component
Factory
ü m m
m
ü m ü
ü
ü m m
UMLStudio
ü m m
ü1)
ü ü ü
ü
m m ü
WithClass 2000
ü m m
ü1)
ü m ü
ü
ü
ObjectDomain R3
ü
ü
2)
2)
Zoom
ClassBuilder
Linux
Reverse
Hierarchie
Engineering
Übersicht
Automatisches
Layout
Open Source
Name
Windows
Anhang B Stand der Technik
ü
1)
m m
m m
ü
1)
Nur beim automatisch erstellten ersten Diagramm
2) Läuft auf allen Java-kompatiblen Systemen
102
Anhang C Inhalt der beigefügten CD-ROM
Anhang C Inhalt der beigefügten CD-ROM
Bilder
Abbildungen
Zeichnungen aus der Diplomarbeit
DiaClassma
Screenshots des Programms DiaClassma
Stand der Technik
Screenshots der Programme zur Analyse „Stand der Technik“
DiaClassma
Das erstellte Programm in ausführbarer Form
DiaClassma Source
Sourcecode des Programms DiaClassma
CPPParser
Komponente Parser
Data
Klassen zur Datenverwaltung
Debug
DiaClassma, kompiliert als Debug-Version
extern
Komponenten von anderen Autoren
sizebar
Komponente CSizingControlBar
hlp
Hilfe-Dateien von DiaClassma
Release
DiaClassma, kompiliert als Release-Version
res
Ressourcen (Icons sowie Bilder für Toolbar und Explorer )
Dokumente
Diplomarbeit in den Formaten OpenOffice und PDF
103
Anhang D Abbildungsverzeichnis
Anhang D Abbildungsverzeichnis
Abbildung 1: Objektorientierte Methoden und Notationen im historischen
Kontext........................................................................................... 9
Abbildung 2: Die drei Amigos: Grady Booch, James Rumbaugh, Ivar Jacobsen
(v.li.n.re.)...................................................................................... 11
Abbildung 3: Four Layer Modeling Architecture von UML.................................13
Abbildung 4: Core Pakete der UML Infrastructure ..............................................14
Abbildung 5: Icons für die Stereotypen Control, Entity, Boundary...................... 15
Abbildung 6: Beispiel für Constraints...................................................................16
Abbildung 7: UML Diagramm mit Kommentar................................................... 18
Abbildung 8: UML Klassen und ein Objekt......................................................... 21
Abbildung 9: UML Assoziationen........................................................................ 22
Abbildung 10: UML Aggregation und Komposition............................................ 23
Abbildung 11: UML Generalisierung................................................................... 24
Abbildung 12: UML Abhängigkeiten .................................................................. 25
Abbildung 13: UML Schnittstellen und Ports.......................................................26
Abbildung 14: UML Parametrisierte Klassen (Templates)...................................27
Abbildung 15: UML Pakete.................................................................................. 29
Abbildung 16: UML Model (a) und Subsystem (b).............................................. 30
Abbildung 17: UML Komponenten...................................................................... 30
Abbildung 18: UML Verteilungsdiagramm.......................................................... 31
Abbildung 19: UML Kompositionsstrukturdiagramm mit Parts und
Kollaborationen............................................................................ 32
Abbildung 20: UML Anwendungsfalldiagramm.................................................. 33
Abbildung 21: UML Aktivitätsdiagramm, entnommen aus [UML2Toolkit]....... 34
Abbildung 22: UML Zustandsdiagramm.............................................................. 34
Abbildung 23: UML Sequenzdiagramm, entnommen aus [UML2Toolkit]..........35
Abbildung 24: UML Interaktionsübersichtsdiagramm......................................... 36
Abbildung 25: UML Timingdiagramm.................................................................37
Abbildung 26: UML Kommunikationsdiagramm.................................................37
Abbildung 27: Screenshot ClassBuilder 2.4 Alpha 1.7.........................................39
Abbildung 28: Screenshot Jumli 1.4..................................................................... 41
Abbildung 29: Screenshot Metamill v3.1 (build 556) ..........................................42
Abbildung 30: Screenshot ObjectDomain R3 (build 292).................................... 44
Abbildung 31: Screenshot objectiF 4.7................................................................. 45
Abbildung 32: Screenshot Rational Rose Enterprise Edition............................... 48
Abbildung 33: Screenshot WithClass 2000 Enterprise 6.0................................... 50
Abbildung 34: Zeiger und Referenzen.................................................................. 58
104
Anhang D Abbildungsverzeichnis
Abbildung 35:
Abbildung 36:
Abbildung 37:
Abbildung 38:
Abbildung 39:
Abbildung 40:
Abbildung 41:
Abbildung 42:
Abbildung 43:
Logische Komponenten................................................................ 59
Funktionsaufrufe in Klassendiagrammen..................................... 61
Methoden...................................................................................... 63
Die Komponenten von DiaClassma..............................................84
Screenshot DiaClassma.................................................................86
Das Kontextmenü im Klassendiagramm von DiaClassma........... 88
Der Parser in DiaClassma, erstellt mit DiaClassma......................89
Baumstruktur des Lexers...............................................................89
Die Datenverwaltung in DiaClassma, erstellt mit DiaClassma.... 91
105
Anhang E Literaturverzeichnis
Anhang E Literaturverzeichnis
[BaEaTaTo99] Guiseppe Di Battista, Peter Eades, Roberto Tamassia, Ioannis G.
Tollis: „Graph Drawing: Algorithms of the Visualisation of
Graphs“, Prentice Hall PTR, Upper Saddle River, New Jersey,
1999
[Beck99]
Kent Beck: „Extreme Programming Explained: Embrace
Change“, Addison-Wesley Professional, ISBN: 0201616416,
1999
[BeCu89]
Kent Beck, Ward Cunningham: „A Laboratory For Teaching
Object-Oriented Thinking“, OOPSLA 1989 Paper
[Booch91]
Grady Booch: „Object-Oriented Design with Applications“,
Benjamin /Cummings Publishing Company Inc. Redwood City,
California, 1991
[Booch94]
Grady Booch: „Object-Oriented Analysis And Design with
Application“, 2nd Edition, Benjamin / Cummings Publishing
Company Inc. Redwood City, California, 1994
[Chen76]
Peter Pi-Shan Chen: „The Entity-Relationship Model. Toward a
Unified View of Data“, ACM Transactions on Database Systems,
1976 ACM-Press ISSN 0362-5915
[ChLeFa93]
Dennis Champeaux, Doug Lea, Penelope Faure: „ObjectOriented System Development“, Addison-Wesley Professional,
ISBN 0-201-56355-X, 1993
[Cole94]
D. Coleman, P. Arnold, S. Bodoff, C. Dollin, H. Gilchrist, F.
Hayes, P. Jeremes: „Object-Oriented Development - The
FUSION Method“, Prentice Hall, Englewood Cliffs, New Jersey,
1994
[Cook94]
Steve Cook, John Daniels: „Designing Object Systems: ObjectOriented Modeling with Syntropy“, Prentice Hall, New York,
1994
[CoPa96]
M. K. Coleman, D. S. Parker: „Aesthetics-based Graph Layout
for Human Consumption“, Software - Practice & Experience, 26
(12):1415-1438, 1996
106
Anhang E Literaturverzeichnis
[CoYo91a]
P. Coad, E. Yourdon.: „Object-Oriented Analysis“, 2nd Edition.,
Prentice Hall, Englewood Cliffs, New Jersey, 1991
[CoYo91b]
P. Coad, E. Yourdon.: „Object-Oriented Design“, Prentice Hall,
Englewood Cliffs, New Jersey, 1991
[DiTa93]
T. Dillon, P.L. Tan: „Object-Oriented Conceptual Modeling“,
Prentice Hall of Australia Pty Ltd. Sydney, 1993
[Eich02]
H. Eichelberger: „Aesthetics of Class Diagrams“, Proc. of the
First IEEE International Workshop on Visualizing Software for
Understanding and Analysis, Vissoft 2002 , S. 23-31, 2002
[Fire93]
D.G. Firesmith: „Object-Oriented Requirements Analysis And
Logical Design: A Software Engineering Approach“, John Wiley
& Sons New York, New York, 1993
[Graham95]
Ian S. Graham, Aan Graham: „Migrating to Object Technology“,
Addison-Wesley, ISBN: 0201593890, 1995
[HeEd94a]
B. Henderson-Sellers, J. Edwards: „The Working Object: ObjectOriented Software Engineering: Methods and Management“,
Prentice-Hall, 1994
[HeEd94b]
B. Henderson-Sellers, J.M. Edwards: „Book Two of ObjectOriented Knowledge: The Working Object: Object-Oriented
Software Engineering: Methods and Management“, Prentice Hall,
ASIN: 0130939803, 1994
[Hend96]
B. Henderson-Sellers: „The OPEN methodology“, Object
Magazine (Nov 1996), 6(9), 56-59, 1996
[HMU02]
John E. Hopcroft, Rajeev Motwani, Jeffrey D. Ullman:
„Einführung in die Automatentheorie, Formale Sprachen und
Komplexitätstheorie“, Pearson Studium, 2002.
[Jaco92]
Ivar Jacobson, M. Christerson, P. Jonsson, G. Övergaard:
„Object-Oriented Software Engineering: A Use Case Driven
Approach“, Addison Wesley, Wokingham, England, 1992
[Jeckle04]
Mario Jeckle, Vortragsfolien „UML 2.0 - Die neue Version der
Standardmodellierungssprache“, http://www.jeckle.de, 2004
[Kruch98]
Philippe Kruchten: „The Rational Unified Process“, AddisonWesley Pub Co, ASIN: 0201604590, 1998
107
Anhang E Literaturverzeichnis
[Lewi90]
J. Lewi, E. Steegmans, S. Van Baelen: „EROOS: EntityRelationship Based Object-Oriented Specifications“, Department
of Computer Science, K.U.Leuven, CW Report 111, Leuven, B,
1990
[Meyer97]
Bertrand Meyer: „Object-Oriented Software Construction“, 2nd
Edition, Prentice Hall, ISBN 0-13-629155-4, 1997
[OpJo90]
William Opdyke, Ralph Johnson: „Refactoring: An aid in
designing application frameworks and evolving object-oriented
systems“, Proceedings of Symposion on Object-Oriented
Programming Emphasizing Practical Applications (SOOPPA),
September 1990
[Robi92]
P. J. Robinson: „Hierarchical Object-Oriented Design“, Prentice
Hall International (UK) Ltd, 1992
[RuGo92]
K. S. Rubin, A. Goldberg: „Object Behaviour Analysis“,
Communications of the ACM, 35(9): 48-62, 9 1992
[Rumb91]
James Rumbaugh, Michael Blaha, William Premerlani, Frederick
Eddy, William Lorensen: „Object-Oriented Modeling And
Design“, Prentice Hall, Englewood Cliffs, New Jersey, 1991
[SeGuWa94]
Bran Selic, Garth Gullekson, Paul T. Ward: „Real-Time ObjectOriented Modeling“, Wiley, ISBN: 0471599174, 1994
[ShMe88]
Sally Shlaer, Stephen J. Mellor: „Object-Oriented System
Analysis - Modeling the World in Data“, Prentice Hall,
Englewood Cliffs, New Jersey, 1988
[ShMe92]
Sally Shlaer, Stephen J. Mellor: „Object-Lifecycles- Modeling the
World in States“, Prentice-Hall, Englewood Cliffs, New Jersey,
1992
[SofDevUML2] Olaf Kath, Eckhardt Holz, Marc Born: „Softwaredevelopment mit
UML 2“, Addison-Wesley, ISBN: 3-8273-2086-0, 2003
[Sully93]
P. Sully: „Modelling the World with Objects“, Prentice Hall
International (UK) Ltd. London, 1993
[TaBaBa88]
R. Tamassia, Guiseppe Di Battista, C. Batini: „Automatic graph
drawing and readability of diagrams“, IEEE Transactions on
Systems, Man and Cybernetics, 18(1):61-79, 1988
108
Anhang E Literaturverzeichnis
[UML2Toolkit] Hans-Erik Eriksson, Magnus Penker, Brian Lyons, David Fado:
„UML 2 Toolkit“, Wiley Publishing, ISBN: 0-471-46361-2, 2004
[W3-B72]
Lucent Technologies Inc.: Users' Reference to B;
http://cm.bell-labs.com/cm/cs/who/dmr/kbman.html;
Zugriff: 25.09.2004
[W3-Bison]
Free Software Foundation, Inc.: Bison (13.08.2004);
http://www.gnu.org/software/bison/bison.html;
Zugriff: 29.09.2004
[W3-CodePro] Code Project: The Code Project – Freier Sourcecode und
Anleitungen (25.09.2004); http://www.codeproject.com;
Zugriff: 25.09.2004
[W3-CRefact] Alejandra Garrido: CRefactory – Refaktorisierungstool für C mit
Unterstützung von Präprozessor-Anweisungen (07.09.2003);
https://netfiles.uiuc.edu/garrido/www/CRefactory.html;
Zugriff: 25.09.2004
[W3-DIN-PAP] DIN 66001: Sinnbilder für Datenfluss- und
Programmablaufpläne; http://www.fhjena.de/~kleine/history/software/DIN66001-1966.pdf;
Zugriff: 28.09.2004
[W3-JECKLE] Mario Jeckle; www.jeckle.de; Zugriff: 25.09.2004
[W3-Manhatt] VERTIGO Engineering: Manhatten – See your code from the
Sky; http://www.vertigoeng.com/manhattan; Zugriff: 25.09.2004
[W3-OMG]
Object Management Group, Inc. (OMG) (24.09.2004);
www.omg.org; Zugriff: 25.09.2004
[W3-OOSE]
oose.de Dienstleistungen für innovative Informatik GmbH
(24.09.2004); www.oose.de; Zugriff: 25.09.2004
[W3-RefThum] Sven Gorts, Philipe T'Seyen: Refactoring Thumbnails;
http://www.refactoring.be; Zugrif:: 27.09.2004
[W3-SF]
OSTG Open Source Technology Group: Sourceforge - Open
Source Software Development Website (24.09.2994);
http://sourceforge.net; Zugriff: 25.09.2004
109
Anhang E Literaturverzeichnis
[W3-SiCoBa]
Data Mekanix: Hompage von CSizingControlBar (31.03.2002);
http://www.datamekanix.com; Zugriff: 25.09.2004
[W3-UML]
Object Management Group, Inc.: Unified Modeling Language
UML (29.03.2004); www.uml.org; Zugriff: 25.09.2004
[WaNe95]
Kim Waldén, Jean-Marc Nerson: „Seamless Object-Oriented
Software Architecture“, Prentice Hall, Englewood Cliffs, New
Jersey, ASIN: 0130313033, 1995
[WiNa95]
Wilkinson, M. Nancy: „Using CRC Cards - An Informal
Approach to Object-Oriented Development“, SIGS Books, New
York, 1995
[Wirf90]
Rebecca Wirfs-Brock, Brian Wilkerson, Lauren Wiener:
„Designing Object-Oriented Software“, Prentice Hall Englewood
Cliffs, New Jersey, 1990
110
Eidesstattliche Erklärung
Hiermit erkläre ich an Eides statt, dass die vorliegende Diplomarbeit ohne
unzulässige Hilfe und nur unter Verwendung der angegebenen Literatur
angefertigt wurde.
Die Arbeit wurde bisher keiner anderen Prüfungsbehörde vorgelegt und auch noch
nicht veröffentlicht.
Frankfurt am Main, den 30. September 2004
______________________________
Carsten Rudolf Stocklöw
Herunterladen