User Requirements GERICHTETER GEWICHTETER GRAPH DESIGNDOKUMENT Softwareentwicklung Praktikum, Übungsbeispiel 1 Gruppe 18 Andreas Hechenblaickner [0430217] Daniela Kejzar [0310129] Andreas Maller [0431289] Gruppe 18 Seite 1
User Requirements Example 1 1 User Requirements 1.1 Aufgabenstellung Es soll ein gerichteter, gewichteter Graph erstellt werden, welcher aus Knoten (nodes) und Kanten (edges) besteht. Hierbei sollten Knoten mit anderen Knoten durch Kanten verbunden werden. Die Datenstruktur sollte verschiedene Funktionalitäten, wie zb das Einfügen und Löschen von Knoten und Kanten, aufweisen. 1.2 Anforderungen Es ist eine geeignete Datenstruktur zu entwickeln, die es ermöglicht die gewünschten Funktionalitäten zu erfüllen. Das Testprogramm sollte diese Funktionalitäten zeigen können. Ein Input-Handler soll die eingegebenen Kommandos verarbeiten und die dazu entsprechenden Operationen am Graphen durchführen. Das Programm muss sinnvoll objektorientiert aufgebaut sein. Seite 2 Gruppe 18
Architectural Design 2 Architectural Design 2.1 Module Das Programm wird in folgende Module aufgeteilt: Name Beschreibung Seite Modul Graph Modul Input-Handler Modul Testprogramm Beschreibt die interne Speicherung eines gerichteten Graphen. Stellt Klassen mit Methoden zur Manipulation der im Graphen gespeicherten Knoten und Kanten zur Verfügung. Verarbeitet eingegebene Kommandos und führt die entsprechenden Befehle aus. Leitet eingegeben Kommandos an den Input-Handler weiter und gibt Fehlermeldungen entsprechend an den Benutzer weiter. 4 7 9 Gruppe 18 Seite 3
Modul Graph Example 1 3 Modul Graph 3.1 Beschreibung Ein gerichteter gewichteter Graph besteht aus beliebig vielen Knoten die untereinander durch beliebig viele Kanten verbunden sein können. Jede Kante hat einen Start- und einen Zielknoten. Start- und Zielknoten können auch gleich sein in diesem Fall zeigt ein Knoten auf sich selbst. Ein Graph, jeder Knoten und jede Kante haben einen Namen. Es dürfen nicht zwei Knoten mit demselben Namen existieren. Es dürfen weder zwei Kanten mit selbem Namen, zwischen zwei Knoten existieren, noch dürfen zwei Kanten mit demselben Namen denselben Knoten als Start- oder Zielknoten haben. Es jedoch erlaubt, dass mehrere Kanten denselben Namen besitzen, vorausgesetz sie teilen sich weder Start- noch Zielknonten. Jede Kante speichert zusätzlich noch eine positive Ganzzahl als Gewichtung. Ein neuer Graph enthält anfangs weder Knoten noch Kanten. ad: 1 bd: 1 ce: 1 Abbildung 1: Beispiel eines Graphen 3.2 Bereitgestellte Klassen Graph, Node und Edge Die Klasse Graph speichert einen gerichteten, gewichteten Graphen. Alle möglichen Operationen wie zb das Einfügen von Knoten und Kanten werden über public-methoden bereitgestellt. Die interne Speicherung der Zuordnung von Kanten und Knoten wird im nächsten Abschnitt Verwendete Datenstruktur beschrieben. Seite 4 Gruppe 18
Modul Graph Displayable Alle anzeigbaren Objekte haben die gemeinsame abstrakte Superklasse Displayable, die eine Implementierung der virtuellen Methode show erfordert. In dieser Methode wird einfach die gewünschte Repräsentation der Klasse als String auf den Standard-Output geschrieben. Abbildung 2: Klassendiagramm des Moduls Graph 3.3 Verwendete Datenstruktur Die Knoten werden intern in einem assoziativen Speicher (Template Map aus der STL) mit dem Name des Knoten und den Knoten selbst als Schlüssel- Werte-Paar gespeichert. Diese Datenstruktur wird verwendet da auf diese Weise einfach verhindert werden kann, das mehrere Knoten mit demselben Namen im Graphen existieren, und die häufige Operation Knoten mit einem bestimmten Namen ermitteln kann ohne zeitaufwändiges durchlaufen aller Knoten durchgeführt werden. Die Kanten werden intern ebenfalls in Maps gespeichert. Es gibt jedoch im Graphen keine gesamte Liste, sondern jeder Knoten hält jeweils zwei Maps von Kanten. Die erste Map speichert alle von diesem Knoten ausgehenden Kanten, die zweite alle Kanten die diesen Knoten als Ziel haben. Jeder Knoten wird somit in genau zwei Listen gleichzeitig gehalten. Zusätzlich weiß jede Kante welche ihre Start- und Zielknoten sind. Durch diese Speicherung wird auch hier einerseits das schnelle Ansprechen einer bestimmten Kante über ihren Namen ermöglicht sowie das Einfügen von zwei gleichnamigen Kanten innerhalb eines Knotens verhindert. Für die korrekte An- und Abmeldung der Kanten bei den entsprechenden Knoten ist die Kante selbst verantwortlich. Wird ein Knoten gelöscht der von einer Kante als Start- oder Zielknoten verwendet wird, so muss auch diese Kante gelöscht werden. Wird eine Kante gelöscht, ist es notwendig die Verweise aus den Listen des Start- und des Zielknotens zu entfernen. Gruppe 18 Seite 5
Modul Graph Example 1 Der zuvor dargestellte gerichtete Graph wird daher folgendermaßen gespeichert: Graph "A" "B" "C" "D" "E" *Node A *Node B *Node C *Node D *Node E Node A "ab" *Edge ab "ad" *Edge ad Source Edges Destination Edges Edge ab Source *Node A Destination *Node B Edge ad Source *Node A Nodes Node B Destination *Node D "bd" *Edge bd "ab" *Edge ab Edge bd "bc" *Edge bc "cb" *Edge cb Source *Node B Source Edges Destination Edges Destination *Node D Node C Edge cb Source *Node C "cb" *Edge cb "bc" *Edge bc Destination *Node B "ce" *Edge ce Source Edges Destination Edges Edge... Node... Abbildung 3: Interne Speicherung eines Graphen 3.4 Fehlerbehandlung Zur Fehlerbehandlung werden folgende Exceptions zur Verfügung gestellt: Exception GraphException NodeAlreadyExistsException EdgeAlreadyExistsException NoSuchNodeException NoSuchEdgeException Beschreibung / Fehlermeldung Abstrakte Basisklasse für alle Fehlerfälle die in einem Graph auftreten können. error: node already exists error: edge already exists in this context error: no such node in graph error: no such edge Seite 6 Gruppe 18
Modul Input-Handler 4 Modul Input-Handler 4.1 Beschreibung Aufgaben des Input-Handlers ist es eingelesene Kommandos zu verarbeiten und die entsprechenden Befehle mit ihren Parametern auszuführen. Ungültige Kommandos und Fehler beim Ausführen der Befehle, zb durch unvollständige/falsche Parameter, werden an das aufrufende Modul gemeldet. 4.2 Bereitgestellte Klassen InputHandler Die Klasse InputHandler verarbeitet eingelesene Kommandozeilen. Diese werden von außen (im Übungsbeispiel vom Modul Testprogramm) mittels der Methode handleinput als String übergeben. Der Input-Handler speichert eine Liste aller möglichen Befehle und deren zugehörige Objekte einer Command-Klasse (Ableitung der Klasse BaseCommand), welche in der virtuellen Methode executecommand die für diesen Befehlt auszuführenden Anweisungen enthalten. Die Klasse InputHandler ist eine abstrakte Basisklasse, die selbst noch keine Befehle kennt, erst ein Objekt der Klasse GraphInputHandler kennt alle für einen Graph relevanten Befehle. Abbildung 4: Klassendiagramm des Input-Handlers Gruppe 18 Seite 7
Modul Input-Handler Example 1 GraphInputHandler Der GraphInputHandler stellt Befehle zur Manipulation eines Graphen zur Verfügung. Zur Konstruktion muss ein Graph-Objekt übergeben werden. Der GraphInputHandler kann folgende Befehle verarbeiten und auf den zugehörigen Graphen anwenden: Kommando add node <name>... add edge <src> <dst> <name> <weigth>... delete node <name>... delete edge <src <dst> <name>... show [<node>] quit Beschreibung Neue(n) Knoten einfügen. Neue Kante(n) einfügen. Bestimmte(n) Knoten löschen. Bestimmte Kante(n) löschen. Alle Kanten die einen bestimmten Knoten als Start- oder Zielknoten haben anzeigen. Wird kein Knoten angegeben, wird er gesamte Graph angezeigt. Veranlasst das aufrufende Modul das Programm zu beenden. 4.3 Fehlerbehandlung Zur Fehlerbehandlung werden folgende Exceptions zur Verfügung gestellt: Exception InputHandlerException InvalidArgumentException UnknownCommandException Beschreibung / Fehlermeldung Abstrakte Basisklasse für alle Fehlerfälle die im Input-Handler auftreten können. error: invalid argument(s) error: unknown command 4.4 Weitere Designentscheidungen StringTokenizer Zur einfachen Verarbeitung der übergeben Befehlszeile wird diese zunächst von einer eigenen Klasse StringTokenizer in ihre einzelnen Wörter zerlegt. Ein Aufruf der Methode getnexttoken liefert immer das jeweils nächste Wort, solange hasmoretoken wahr zurückliefert. Seite 8 Gruppe 18
Modul Testprogramm 5 Modul Testprogramm 5.1 Beschreibung Das Testprogramm stellt Befehle zur Verfügung mit der ein Graph bearbeitet werden kann. Dazu wird zunächst ein leerer Testgraph erstellt. Es wird eine Eingabeauforderung angezeigt, in der der Benutzer eine Reihe von durch das Modul Input-Handler definierte Befehle zur Manipulation eines Graphen eingeben kann. Das Testprogramm ist dafür verantwortlich das alle vom Graphen und vom Input-Handler weitergereichte Exceptions durch ihre entsprechende Fehlermeldungen dargestellt werden. Das Testprogramm besteht lediglich aus der Klasse TestProgram und wird über die Methode run vom Hauptprogramm aus gestartet. 5.2 Testfälle Um das Programm automatisch auf Fehler zu überprüfen, wird eine Liste von Befehlen in einer Datei bereitgestellt. Diese können dann einfach mittels workflow < testinput.txt in das Programm eingelesen und verarbeitet werden. Danach lässt sich die Ausgabe mit einer zuvor händisch erstellten Referenz-Ausgabe vergleichen. Die erledigt zum Beispiel das Programm diff und kann somit bequem zb in das Makefile eingebaut werden. Verschieden Testfälle werden mit anderen Gruppen gemeinsam erarbeitet und ausgetauscht. Gruppe 18 Seite 9