Sächs. VWA Fritzsche: Software-Engineering Inhalt 1 1.1 1.2 Ziele und Arbeitsmethoden der Softwaretechnologie Methodologie der Softwareentwicklung Die Modellierungssprache UML 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 2.12 Objektorientierte Modellierung Klassen-Instanzen-Abstraktion Attribute und Operationen Sichtbarkeit und Lebensdauer Assoziationen und Verknüpfungen Aggregation und Komposition Generalisierung und Spezialisierung Vererbung und Delegation Polymorphie Abstrakte und konkrete Klassen Instanzen als Laufzeitobjekte Statechart-Modelle Aktivitäts-Diagramme 3 3.1 3.2 3.3 3.4 3.5 Implementation von UML-Modellen Von UML nach Java Ein Beispiel: Baum-Klassen Interfaces und Mehrfachvererbung Implementation von Assoziationen und Aggregationen Dynamische Modelle und Event-Handling 4 4.1 4.2 4.3 Systemarchitekturen Komponentenmodelle Verteilungsmodelle Objektserialisierung 5 5.1 5.2 5.3 5.4 5.5 Der Softwareentwicklungsprozess Anwendungsfall-Analyse Methodische Aspekte der Modellierung Qualitätssicherung Projektmanagement und Prozessmodellierung Konfigurationsmanagement 6 6.1 6.2 6.3 6.4 Wiederverwendung Komponentenbasierte Softwareentwicklung - Frameworks Entwurfsmuster Grafische Benutzeroberflächen Die Model-View-Controller-Architektur 1 Sächs. VWA Fritzsche: Software-Engineering Literatur H. Balzert Lehrbuch der Objektmodellierung. Analyse und Entwurf mit der UML 2. Spektrum Akademischer Verlag, 2004 M. Fowler, K. Scott UML konzentriert. Eine Einführung in die Standard-Objektmodellierungssprache. Addison-Wesley, 2. Aufl. 2000 C. Rupp, S. Queins, B. Zengler UML 2 glasklar, Praxiswissen für die UML-Modellierung Carl Hanser Verlag, 3. Aufl., 2007 B. Oestereich Objektorientierte Softwareentwicklung mit der Unified Modeling Language. Oldenbourg, 3. Aufl., 1997 W. Pree Komponentenbasierte Softwareentwicklung mit Frameworks. dpunkt.verlag, 1997 I. Sommerville Software Engineering Pearson Studium - IT, 9. aktualisierte Auflage, 2012 Entwicklungsplattform Eclipse: http://www.eclipse.org Eclipse Dokumentation (Kepler release): http://www.eclipse.org/kepler Eclipse Dokumentation (Mars release): http://www.eclipse.org/mars Entwicklungsplattform Topcased: http://topcased.org 2 Sächs. VWA Fritzsche: Software-Engineering 3 1 Einführung: Ziele und Arbeitsmethoden der Softwaretechnologie 1.1 Methodologie der Softwareentwicklung Die Softwaretechnologie (Software Engineering) ist die Lehre vom Prozess der organisierten Softwareproduktion. Sie umfasst (die Erforschung von) Theorie, Methoden und Werkzeuge(n) zur Herstellung von Software. Das schließt Maßnahmen zur Qualitätssicherung, die Nachnutzung von Software sowie das Projektmanagement und das Konfigurationsmanagement ein. Dem Prozess der Softwareentwicklung wird im konkreten betrieblichen Umfeld jeweils ein Lebenszyklusmodell (auch als Vorgehensmodell bezeichnet) zugrunde gelegt. Bekannte Lebenszyklusmodelle sind u.a. das Wasserfallmodell und das Spiralmodell (letzteres ist ein evolutionäres Modell). Ein sehr einfaches Modell ist das V-Modell. Für kleinere Entwicklergruppen ist die „Agile Softwareentwicklung“ ein bevorzugtes Modell. Im Entwicklungsprozess lassen sich Entwicklungsphasen abgrenzen: • • • • Analyse (Anforderungsanalyse (Pflichtenheft) – Anwendungsfallanalyse – Problembereichsanalyse) , Entwurf (Softwareentwurf – Algorithmenentwurf – Komponentenentwurf – Modelloptimierung) , Implementation (reduziert sich z.T. auf eine Generierung von Code bei Vorliegen formaler Spezifikationen) und Test (Unit-Test, Integrationstest, Systemtest; teilweise auch entwicklungsbegleitend). Es existiert nicht eine allgemein anerkannte Entwicklungsmethodik, sondern eine Methodologie (d.h. Lehre von den Methoden ...) der Softwareentwicklung. Wichtige Aspekte der Softwaretechnologie sind - 1.2 die Entwicklung „großer“ Systeme (eine Entwicklergruppe arbeitet über längere Zeit an einem Vorhaben) Rollenbindung beteiligter Personen Erweiterung und Anpassung (Entwicklung endet nicht mit 1. Systemversion) Wiederverwendung (Entwicklung beginnt nicht erst beim eigenen System) Software-Architekturen (abhängig von eingesetzten Entwicklungsmethoden) CASE (rechentechnische Unterstützung ist abhängig von angewandten Entwicklungsmethoden) Zweckbindung führt auf Musterarchitekturen (z.B. Datenbank-Anwendungen) Die Modellierungssprache UML Die Unified Modeling Language (UML) ist eine grafische Modellierungs- und Entwurfssprache. Sie basiert auf Entwicklungen von G. Booch, J. Rumbaugh sowie I. Jacobson und bildet seit Anfang der 1990er Jahre einen Quasi-Standard im Software Engineering. Mit der UML (aktuell: UML 2) werden Modelle entwickelt, die Ausschnitte einer zu modellierenden realen oder erdachten Welt widerspiegeln. Zur Darstellung der Modelle unterschiedlicher Art dienen jeweils Diagramme. Diagramme visualisieren und formalisieren Ausschnitte aus bzw. Sichten auf Modelle. Sächs. VWA Fritzsche: Software-Engineering Modelltypen: • • • • • • Use-Case-Modell Klassenstrukturmodell Interaktionsmodell (Sequenz- u. Kommunikations-Diagramm) Zustandsmodell (Zustands- u. Aktivitäten-Diagramm) Komponentenmodell Verteilungsmodell Charakterisierung der grafischen Modellierungssprache: In der UML-Notation werden 4 Arten grafischer Konstrukte verwendet: Icons, 2-dimensionale Symbole, Pfade und Strings. Im Sinne eines Graphen bilden zweidimensionale Symbole Knoten, die durch Pfade als Kanten verbunden sein können. Strings besitzen (mit Ausnahme von Kommentaren) eine Syntax, sie können singulär auftreten oder anderen Sprachelementen zugeordnet sein. Die UML ist eine (semi-)formale Sprache. Die Definition der Syntax (und evtl. der Semantik) einer formalen Sprache erfolgt mit den Mitteln einer (formalen) Sprache. Diese Sprache heißt Metasprache und dient zur Definition einer Objektsprache. Die UML wird auch als Metasprache zur Modellierung von UML-Konzepten selbst eingesetzt (sog. Metamodellierung). 2 Objektorientierte Modellierung 2.1 Klassen-Instanzen-Abstraktion Ein Objekt ist ein (abstrakter) Gegenstand oder ein Konzept mit klarer Abgrenzung und präziser Bedeutung. Beispiele: Kommissar Rex, das Fenster im Haus oben links, Herr Lutze, Mediatec GmbH, ... Eine Klasse beschreibt eine Menge von gleichartigen Objekten, die • • • ähnliche Struktur und ähnliche Merkmale (Attribute) besitzen, ähnliches Verhalten zeigen (Operationen bzw. „Methoden“), in Relationen zu Objekten anderer Klassen oder der gleichen Klasse stehen. Eine Klasse kann durch Aufzählung aller zugehörigen Objekte oder durch die Definition der Eigenschaften der Objekte (Klassendefinition) beschrieben werden. Zu einer Klasse gehörige Objekte werden alternativ auch als Instanzen, Instanzobjekte oder Exemplare der Klasse bezeichnet. Klassen werden benannt, besitzen also einen Namen. Beispiele: Hund, Fenster, Person, Firma, ... Objekte bilden bei der Realisierung auf einem Rechner Implementierungseinheiten. Objekte sind immer Instanzen einer bestimmten Klasse und besitzen eine Identität und eine Lebensdauer. Jedes Objekt kennt die Klasse, zu der es gehört (Instanzierungsrelation). Eine Klassendefinition umfasst (evtl. implizit) die Beschreibung von Vorschriften zur Erzeugung von Instanzen. Klassenbildung ist immer Abstraktion. Bei einer Abstraktion werden für den Zweck der Betrachtung unwesentliche Eigenschaften weggelassen; sprich: „es wird von unwesentlichen Eigenschaften abstrahiert“. Abstraktion ist in der Informatik ein mächtiges Mittel zur Bewältigung von Komplexität. 4 Sächs. VWA Fritzsche: Software-Engineering 2.2 Attribute und Operationen Attribute beschreiben die betrachteten strukturellen Merkmale, die alle zu einer Klasse gehörenden Instanzen in einer gewissen spezifischen Ausprägung besitzen. In einer Klassendefinition sind diese Attribute benannt. Jedes einzelne Objekt der Klasse besitzt für jedes Attribut (Merkmal) eine individuelle Ausprägung (Merke: Objekt - Merkmal -Ausprägung, also „OMA“), die sich allerdings bei einem Objekt im Laufe der Zeit ändern kann. „Ausprägungen“ sind Datenwerte eines in einer Klassendefinition vorbestimmten Datentyps. Der Wertebereich ist für jedes Attribut (eventuell impliziert) in der Klassendefinition durch Angabe eines Typnamens festgelegt. Jedes existierende Objekt einer Klasse besitzt für jedes Attribut zu einem bestimmten Zeitpunkt einen Wert aus dem Wertebereich. Der Wert für ein bestimmtes Attribut für ein Objekte kann von den bei anderen Objekten zum selben Zeitpunkt geltenden Werten verschieden sein. Die Gesamtheit der Werte (d.h. der Ausprägungen) aller Attribute eines Objektes zu einem bestimmten Zeitpunkt bildet den Zustand dieses Objektes. Operationen definieren das Verhalten von Objekten. Alle Instanzobjekte einer Klasse verhalten sich in einer gewissen Weise gleichartig. Das Verhalten eines Objektes wird dabei von den individuellen Ausprägungen der Attribute mitbestimmt. Die Verhaltensbeschreibung wird der Klassendefinition zugeordnet, und nicht jedem Objekt einzeln. Sie existiert damit einmal unabhängig von der Anzahl vorhandener Instanzen, was bei der Implementation von Operationen in einer objektorientierten Programmiersprache genauso umgesetzt wird. Als „Methode“ bezeichnet man die Implementation einer Operation für eine Klasse. Eine Methode kann jeweils auf ein Instanzobjekt angewendet werden. Eine spezielle Art der für eine Klasse definierbaren Operationen sind Konstruktoren. Ein Konstruktor dient zur Erzeugung von Instanzobjekten, er wird also nicht auf ein bereits bestehendes Instanzobjekt angewendet. Eine Klasse wird in der UML durch ein Rechteck mit maximal drei Feldern für den Klassennamen, die Attribute und die Operationen dargestellt. Klassennamen sollen fett gedruckt sein und mit Großbuchstaben beginnen, die Namen von Attributen und Operationen beginnen mit Kleinbuchstaben. Instanzobjekte werden ebenfalls als Rechtecke mit maximal zwei Feldern dargestellt. Instanzobjekte können einen Namen besitzen oder auch unbenannt sein. Im obersten Feld wird – wenn vorhanden - der Instanzname gefolgt von Doppelpunkt, gefolgt vom Klassennamen angegeben. In jedem Fall wird aber ein Doppelpunkt und nachfolgend der Klassenname angegeben. Klassen können in einem Klassenstruktur-Diagramm dargestellt werden. Objektstrukturen können in Objektdiagrammen dargestellt werden. Ein Objektdiagramm stellt eine „Momentaufnahme“ zur Laufzeit eines Systems dar. In einigen Softwareentwicklungsumgebungen können Objekte und Klassen gemeinsam in einem Diagramm dargestellt werden. Eine gestrichelte Linie mit Pfeil zur Klasse symbolisiert in diesem Fall die Beziehung zwischen Instanz und Klasse. Notation einer Klasse: 5 Sächs. VWA Fritzsche: Software-Engineering 6 klassenname [visibility] attributname : typname [= default-wert] … … [visibility] operationsname ( argumentliste) : ergebnistypname … … Notation einer Instanz: [instanznname] : klassenname attributname = wert … … Beispiel mit Angabe der Instanzierungsrelation: Beachte: 'max' in 'max:Person' benennt das Objekt. Diese Benennung ist vom Namen bzw. dem Vornamen einer modellierten Person zu unterscheiden. Das Objekt könnte auch mit 'p1' benannt sein, dann wäre p1:Person anzugeben. 2.3 Sichtbarkeit und Lebensdauer Benennungen ordnen Klassen, Objekten, Attributen und Operationen Namen zu. Namen sind immer innerhalb einer gewissen Umgebung (Environment) sichtbar, d.h. unter Verwendung der Namen können die jeweiligen Dinge angesprochen werden. Außerhalb der Umgebung sind die entsprechenden Dinge nicht ansprechbar, d.h. nicht referenzierbar. Es werden vier relative Sichtbarkeitsbereiche (sog. Visibility) abgegrenzt: + # ~ public private protected package wide (Packages werden weiter unten beschrieben) Sächs. VWA Fritzsche: Software-Engineering Objekte werden immer mittels Konstruktoren erzeugt. Objekte existieren solange, solange sie referenzierbar sind oder bis sie explizit aus der Objektwelt entfernt werden. Attribute können auf Instanzobjekte (instance scope) oder auf Klassen (class scope) bezogen sein. Im Falle des Bezuges auf Klassen spricht man auch von „Klassenvariablen“. Operationen können auf Instanzobjekte (instance scope) oder auf Klassen (class scope) angewendet werden. Im Falle der Anwendung auf Klassen spricht man auch von „Klassen-Methoden“. 2.4 Assoziationen und Verknüpfungen Eine Assoziation ist eine abstrakte, d.h. nicht näher charakterisierte Beziehung zwischen zwei (binäre A.) oder mehreren Klassen. In der grafischen Darstellung werden die Symbole der beteiligten Klassen durch eine durchgezogene Linie verbunden. Die Enden einer Assoziationslinie können auch mit demselben Klassensymbol verbunden sein. Assoziationen höherer Ordnung (ternäre, ...) werden durch einen Rhombus dargestellt, der durch Linien mit den Symbolen der beteiligten Klassen verbunden ist. Assoziationen höherer Ordnung sollten praktisch nach Möglichkeit vermieden werden. Sie sind schwerer zu verstehen, darzustellen und zu implementieren als binäre Assoziationen. Einer Assoziation kann im Klassenstrukturdiagramm ein Name oder sogar eine Klasse zugeordnet sein. Weitere Eigenschaften können durch Constraints ausgedrückt werden. Eine Verknüpfung (Link) ist ein Element einer Assoziation. Sie wird im Objektdiagramm dargestellt und setzt eine Anzahl von Objekten entsprechend der Ordnung der Assoziation in Beziehung. Ist durch eine binäre Assoziation eine Klasse mit sich selbst verbunden, können unterschiedliche Objekte dieser Klasse miteinander verknüpft sein oder ein Objekt mit sich selbst. Beispiele: 7 Sächs. VWA Fritzsche: Software-Engineering In einer Assoziation können die beteiligten Klassen Rollen spielen. An den Enden des die Assoziation kennzeichnenden Pfades können jeweils der Rollenname der Klasse und eine Kardinalität notiert werden. Durch die Angabe einer Kardinalität wird die Multiplizität beschrieben. Die Angabe erfolgt durch die Aufzählung von Bereichen, die durch Komma getrennt werden. Eine Bereichsangabe hat die allgemeine Form untere-grenze .. obere-grenze. Ein * kann für eine nicht negative ganze Zahl zur Kennzeichnung einer nach oben offenen Grenze gesetzt werden. 2.5 Aggregation und Komposition Eine Aggregation ist eine Sonderform der Assoziation mit zusätzlicher Semantik und bedeutet eine „Teil-Ganzes“-Beziehung. Objekte, die Komponenten einer Sache repräsentieren, sind mit einem Objekt verknüpft, das die Komponentengruppe (das Aggregat) repräsentiert. Aggregation ist über eine beliebige Anzahl von Ebenen hinweg möglich. Darstellung: hohle Raute an dem Ende der Assoziation, das dem Aggregat zugeordnet ist. 8 Sächs. VWA Fritzsche: Software-Engineering Ein Objekt der Klasse mit dem Namen Class1 repräsentiert die Komponentengruppe, Objekte der Klasse Class2 repräsentieren die Komponenten. Eine Komponentengruppe mit Komponenten verschiedener Typen hat entsprechend viele Aggregationen. Eine Aggregation ist transitiv und antisymmetrisch. Die Komposition wird als "enge" Aggregation verstanden, die eine Verbindung der Teile mit dem Ganzen auch hinsichtlich der Lebenslinie bedeutet. Attribute einer Klasse können als Komposition zwischen der Klasse und den Klassen (bzw. Typen) der Attribute verstanden werden. Darstellung: Ausgefüllte Raute anstelle der hohlen Raute. Beispiele: Ein Dokument besteht aus einer Anzahl von Absätzen, die ihrerseits aus mindestens einem Satz bestehen. Ein Polygon besitzt mindestens drei Eckpunkte. Die Farbe und andere Eigenschaften (die Textur) sind dem Polygon fest zugeordnet und existieren nicht ohne das Polygon. Ein Punkt existiert auch unabhängig vom Polygon, er kann z.B. mehreren Polygonen gleichzeitig zugeordnet sein. 2.6 Generalisierung und Spezialisierung Generalisierung und Spezialisierung sind Vorgänge, die eine Inklusions-Relation zwischen (mindestens) einer allgemeineren und (mindestens) einer spezielleren Entität etablieren. Eine speziellere Entität besitzt alle Eigenschaften der allgemeineren Entität (Inklusion) sowie zusätzliche und/oder anders ausgeprägte Eigenschaften (sog. „is a“-Relation, deutsch etwa „ist eine Art von“). Generalisierung und Spezialisierung werden zunächst auf Klassen angewendet. Die Inklusionsrelation ist gerichtet. Sie ist transitiv und antisymmetrisch. Eine an der Relation beteiligte allgemeinere Klasse wird als Superklasse (Oberklasse), eine speziellere Klasse als Subklasse (Unterklasse) bezeichnet. Sprechweise: Ein Hund "ist eine Art von" (is-a) Säugetier. 9 Sächs. VWA Fritzsche: Software-Engineering 10 Die Klasse Hund ist Subklasse zur Klasse Säugetier. Ein Student "ist ein(e)" Person. Die Klasse Person ist Superklasse zur Klasse Student. Eine Subklasse hat gegenüber der zugeordneten Superklasse veränderte Eigenschaften. Dabei kann es sich um - zusätzliche Attribute, zusätzliche Operationen, Modifikationen bzw. spezielle Ausprägungen von Operationen, Modifikationen von Festlegungen für Anfangswerte von Attributen bzgl. der Instanzierung handeln. Durch Generalisierung bzw. Spezialisierung entstehen bzgl. der Inklusionsrelation zyklenfreie, gerichtete Graphen. In einer Klassenhierarchie kann eine Klasse nur eine Superklasse haben. In einer Klassenheterarchie kann eine Klasse mehrere Superklassen haben. Eine Klasse mit mehr als einer Superklasse heißt Vereinigungsklasse. Grafische Notation: Class3 Class4 Class5 Die Spitze des Dreiecks zeigt zur Superklasse. Constraints Für eine Generalisierung können Constraints angegeben werden (neben dem Dreieck in geschweiften Klammern notiert). Vordefinierte alternativ verwendbare Angaben sind overlapping vs. disjoint sowie complete vs. incomplete. Sind alle Subklassen angegeben (die Anordnung ist vollständig, es werden keine weiteren Subklassen erwartet) wird complete angegeben, andernfalls incomplete. Der implizit angenommene Standardwert (d.h. falls die Angabe fehlt) ist incomplete. Kann ein Nachkomme von mehr als einer Subklasse abgeleitet werden, kann dies durch overlapping gekennzeichnet werden. Das Gegenteil kann durch disjoint gekennzeichnet werden. Beispiel: Fahrzeuge können sowohl nach der Antriebsart (motorgetrieben, windgetrieben) als auch nach dem Medium, auf bzw. in dem sie sich bewegen (Luft, Wasser, Land), Sächs. VWA Fritzsche: Software-Engineering spezialisiert werden (vgl. Abbildung Abschnitt 3.3). Ein Lastkraftwagen (LKW) ist demnach ein Fahrzeug, das motorgetrieben ist und sich auf dem Land bewegt. 2.7 Vererbung und Delegation Als Vererbung (inheritance) wird die Weitergabe von Eigenschaften (Attribute, Operationen, Standardinitialwerte) einer Klasse an die Subklassen bezeichnet. Die Beschreibungen von Eigenschaften der Instanzen der Klasse werden entlang der Inklusionsrelation (an die Subklassen) vererbt. Tatsächlich müssen bei einer Klasse die Eigenschaften ihrer Superklasse(n) nicht noch einmal beschrieben werden, sie gelten implizit. Von besonderem Interesse ist, dass das auch bzgl. der Implementation gilt. Durch Mehrfachvererbung ist es möglich, dass eine Klasse, die mehrere Oberklassen hat, Merkmale von allen Oberklassen erbt. Ein Merkmal aus der gleichen Vorfahrenklasse, das in mehr als einem Pfad gefunden wird, wird nur einmal geerbt, es handelt sich um das gleiche Merkmal. Konflikte zwischen parallelen Definitionen führen zu Mehrdeutigkeiten, die bei der programmtechnischen Realisierung gelöst werden müssen. Delegation ist ein Implementierungsmechanismus, mit dessen Hilfe ein Objekt eine Operation auffängt und an ein anderes Objekt zur Ausführung sendet. Zum Beispiel hat eine Klasse A ein Attribut, dessen Wertebereich die Instanzen einer Klasse B umfasst. Ein Instanzobjekt der Klasse A verwaltet (eine Referenz auf) ein Objekt der Klasse B. Eine auf ein Objekt von A angewendete Methode kann damit ihrerseits (in ihrer Implementierung) eine entsprechende Methode auf das verwaltete Objekt von B anwenden. Operationen werden aber nicht automatisch über die Aggregation hinweg vererbt! Mittels Aggregation und Delegation kann fehlende Mehrfachvererbung umgangen werden. Es bestehen folgende Möglichkeiten: Delegation durch Verwendung von Rollenaggregation Vererbung der wichtigsten Klasse und Delegation des Restes Verschachtelte Generalisierung 2.8 Polymorphie Polymorphie bedeutet Vielgestaltigkeit. Der Begriff wird hier verwendet, um hervorzuheben, dass mit demselben Namen zu unterschiedlichen Zeitpunkten unterschiedliche Methoden (d.h. unterschiedliche Implementationen von Operationen) benannt werden können. Beispiel: Wir betrachten Unruhen (mobiles) und möchten mit einer Operation balancedp() bestimmen, ob sich eine Unruhe im Gleichgewicht befindet. Abstrahiert man von den physikalischen Gegebenheiten, handelt es sich bei den Unruhen um eine spezielle Art gewichtsbalancierter Bäume (im Unterschied zu höhenbalancierten Bäumen). Wir modellieren eine Klasse Mobile mit den Attributen "linker Nachfolger" (lsucc) und "rechter Nachfolger" (rsucc). Die Attribute sind vom Typ Mobile, d.h. entsprechende Attribute können Referenzen auf Objekte vom Typ Mobile aufnehmen. Die Armlängen (links bzw. rechts von der Aufhängung) spielen zunächst keine Rolle. Es wird angenommen, sie sind für jede Unruhe links und rechts gleich groß. 11 Sächs. VWA Fritzsche: Software-Engineering Jede Unruhe verwaltet ein Objekt vom Typ Node. Das Attribut key der Klasse Node ist vom Typ int (ganze Zahl) und dient zur Identifikation einer Unruhe. Nur für Unruhen ohne Nachfolger (d.h. für Blattkelemente im Baum der Unruhen) enthält das Attribut key das Gewicht. Bei Unruhen mit Nachfolgern (inneren Knoten) wird key der Wert 0 zugewiesen. Wird die Operation weight auf ein Objekt vom Typ Mobile angewendet, liefert sie bei Blattelementen den Wert von key und sonst die Summe der Gewichte der Nachfolgerknoten. Die Klasse MobileA spezialisiert die Klasse Mobile in der Weise, dass die Arme einer Unruhe unterschiedlich lang sein können. Zur Speicherung der Armlängen dienen die Attribute larm und rarm. Die Attribute lsucc und rsucc werden von Mobile an MobileA vererbt. Die Methode balancedp() kann sowohl auf Instanzen von Mobile als auch von MobileA angewendet werden. Für jeden dieser Fälle wird in Abhängigkeit vom Typ des aktuellen Objekts allerdings eine andere Methode ausgeführt. Da in diesem Fall zur Laufzeit entschieden wird, welche Methode tatsächlich ausgeführt wird, heißt diese Art der Polymorphie "dynamische Polymorphie". Bezogen auf die Implementation spricht man von „später Bindung“ oder auch „dynamischer Bindung“. Die öffentlichen Methoden insKeyWeight, insLsucc, insRsucc, insLarm und insRarm dienen der Konstruktion von Unruhen. 2.9 Abstrakte und konkrete Klassen Eine abstrakte Klasse ist eine Klasse, die selbst keine direkten Instanzen besitzt. Eine abstrakte Klasse wird modelliert mit der Absicht, Subklassen zu schaffen, die Merkmale und Verhalten hinzufügen bzw. konkretisieren. Zu den als Nachkommen geschaffenen Klassen werden dann Instanzen gebildet. Eine konkrete Klasse ist eine Klasse, die direkte Instanzen besitzen kann ("instanziierbare Klasse"). Nur konkrete Klassen können Blattklassen im Vererbungsbaum sein! Eine abstrakte Klasse kann sowohl abstrakte Operationen als auch nicht abstrakte Operationen definieren. 12 Sächs. VWA Fritzsche: Software-Engineering Eine Operation kann abstrakt sein. Eine abstrakte Klasse definieret in diesem Fall nur die Signatur einer Operation, ohne eine entsprechende Methode zu implementieren. Eine Kennzeichnung im Klassensymbol erfolgt entweder durch den Zusatz {abstract} oder dadurch, dass die Signatur der Operation in italics gesetzt wird. Ein Auftreten der Signatur der Operation in einer abgeleiteten Klasse (ohne Kennzeichnung als abstrakte Operation) zeigt an, dass die abgeleitete Klasse eine Methode für die Operation definiert. 2.10 Instanzen als Laufzeitobjekte Ein Objekt ist als Instanz einer Klasse ein Laufzeitobjekt. Es belegt bei der programmtechnischen Realisierung Speicherplatz, hat eine Identität und besitzt eine gewisse Lebensdauer. Objekte können benannt werden. Das bedeutet, dass eine Assoziation zwischen einem Namen (einem Bezeichner) und einer Objektreferenz aufgebaut wird. Namen sind einem Sichtbarkeitsbereich (scope) zugeordnet. Wir unterscheiden (vgl. Abschnitt 2.3): - global - Paket-lokal - Klassen-lokal (evtl. inclusive Vererbung) - Methoden-lokal Im Laufe der Zeit können sich solche Assoziationen ändern, d.h. ein Name kann mit einer Referenz auf ein anderes Objekt assoziiert werden. Typkompatibilität bedeutet, dass einem Bezeichner, der mit einer Referenz auf ein Objekt eines bestimmten Typs (z.B. einer Klasse) assoziiert ist, ein Objekt eines anderen Typs (einer anderen Klasse ("Referenzklasse")) zugeordnet werden kann. Typkompatibilität wird in UML nicht speziell unterstützt. Welche Typen kompatibel sind, hängt bei der programmtechnischen Realisierung von der verwendeten objektorientierten Programmiersprache ab. Die Erzeugung eines Objektes kann auf zweierlei Weise erfolgen: 1. statisch, mit der Initialisierung des Anwendungssystems (per „Compilezeit“) oder 2. dynamisch, durch Anwendung eines Konstruktors der entsprechenden Klasse während der Ausführung einer Methode (per „Laufzeit“). 13 Sächs. VWA Fritzsche: Software-Engineering Objektzustände: vordefinierte Ausprägungen für Zustände von Objekten sind • • • • • nicht existent (eigentlich ein Widerspruch!) existent: Für ein Objekt wurde Speicher erfolgreich angefordert, die Zuordnung der Methoden aus der Klassendefinition war erfolgreich. initialisiert deinitialisiert gelöscht Für persistente Objekte werden zusätzliche Zustände erklärt: ausgelagert, im-Speicher. Ein ausgelagertes Objekt ist nicht im Adressraum des Arbeitsspeichers verfügbar, kann aber wieder in den Arbeitsspeicher geladen werden. Das Erzeugen und Löschen von Objekten kann in Sequenzdiagrammen dargestellt werden. In vielen objektorientierten Programmiersprachen kann der Zeitpunkt zum Löschen eines Objektes vom Programmierer selbst geplant werden (z.B. C++). Objekte können miteinander mittels Nachrichten kommunizieren (senden und empfangen). Ein Objekt kann seinen Zustand verändern infolge von Reaktionen auf Methodenaufrufe (method invocation) Ereignisse (events) Ein Sequenzdiagramm zeigt das Verhalten von Objekten entlang einer Zeitachse („Lebenslinie“ des Objekts) und berücksichtigt dabei die Interaktion mit anderen Objekten durch den Austausch von Nachrichten. Mit Hilfe von Sequenzdiagrammen werden Szenarios beschrieben. Szenario: Folge von Ereignissen, die bei einer ganz bestimmten Ausführung eines Systems auftritt. Es kann alle Ereignisse eines Systems betreffen oder Ereignisse, die bestimmte Objekte eines Systems beeinflussen oder von bestimmten Objekten erzeugt werden. Ein Szenario erhält man durch Aufzeichnung bei der Ausführung eines Systems oder durch gedankliche Vorausplanung der Aufzeichnung. Im Sequenzdiagramm ist eine Aufteilung der Lebenslinie eines Objekts in parallele Zweige möglich. Parallele Lebenslinien können an einem späteren Punkt wieder zusammengeführt werden. 14 Sächs. VWA Fritzsche: Software-Engineering Object1 15 Object2 1: nachricht() 1.1: antwort() 2.11 Statechart-Modelle Ein Zustandsautomat (das ist ein endlicher Automat) ist ein Graph, bestehend aus Zuständen und Transitionen, der die Reaktion eines Objektes einer Klasse auf den Empfang von "äußeren Stimuli" beschreibt. Ein Zustandsautomat ist einer Klasse oder einer Methode zugeordnet. Ein Zustand (state) ist die Verfassung (im Verlaufe des Lebens) eines Objektes, in der es einer Bedingung genügt, eine Aktion ausführt oder auf ein Ereignis wartet. Ein Objekt verweilt eine endliche Zeit in einem Zustand. Eine Transition ist eine durch ein Ereignis verursachte Zustandsänderung eines Objektes. Notation: event(argument ...) [ bedingung ]/ operation(argument ...) Ereignisse (events) sind bemerkenswerte Vorkommnisse. In Bezug auf Statecharts ist ein Ereignis ein Vorkommnis, das eine Transition auslöst. Es hat selbst keine Dauer. Der Zustand eines Objekts bestimmt die Reaktion des Objekts auf ankommende Ereignisse. Reaktionen können Aktionen und/oder Zustandsänderungen sein. Aktionen (actions) sind atomar und nicht unterbrechbar. Ein Zustand kann mit einer andauernden Aktivität verbunden sein. Eine solche Aktivität wird selbst als Zustandsautomat ausgedrückt. Eine andauernde Aktivität kann als Paar von Aktionen ausgedrückt werden. Notation: Ein Statechart (Zustands-Diagramm) repräsentiert einen Zustandsautomaten. Zustände werden durch Zustandsymbole (Rechteck mit abgerundeten Ecken) repräsentiert. Sie können einen Namen beinhalten und optional durch horizontale Linien in bis zu drei Bereiche geteilt werden. Spezielle Symbole existieren für den Startzustand und Endzustände: Sächs. VWA Fritzsche: Software-Engineering 16 Transitionen sind Zustandsübergänge und werden durch Pfeile dargestellt, die Zustandssymbole verbinden. Ereignisse, die Zustandsübergänge auslösen, werden als Beschriftung an die zugehörigen Pfeile geschrieben. Interpretation: • wenn sich ein Objekt in einem Zustand befindet, und ein Ereignis tritt auf, mit dem eine seiner Transitionen beschriftet ist, geht das Objekt in den Zustand am Ende der Transition über: Die Transition „feuert“. • wenn ein Ereignis auftritt, für das es keine vom aktuellen Zustand ausgehende Transition gibt, wird das Ereignis ignoriert. Übergänge ohne Ereignisbeschriftung werden automatisch ausgelöst, sobald die mit dem Zustand verbundenen Aktionen/Aktivitäten abgeschlossen sind. Ereignisse können mit Bedingungen verknüpft sein. Die an ein Ereignis geknüpften Bedingungen müssen erfüllt sein, damit der erwartete Zustandswechsel erfolgen kann. Beispiel: Zustände können auch Subdiagramme enthalten. Zustände können in sequentielle parallele Unterzustände dekomponiert werden. Eine Verfeinerung ist jeweils nur auf einem dieser beiden Wege möglich. Sächs. VWA Fritzsche: Software-Engineering 17 2.12 Aktivitäts-Diagramme Das Aktivitäts-Diagramm ist eine spezielle Art des Zustandsdiagramms, das ausschließlich Aktivitäten und Übergänge zwischen diesen zeigt. Eine Aktivität ist einem Zustand zugeordnet und repräsentiert eine andauernde interne Aktion. Mehrere von einer Aktivität ausgehende Transitionen werden durch Bedingungen unterschieden: Activity3 [x > 0] Activity1 Activity2 [x = 0] [x < 0] Activity4 Activity2 Activity3 [x = 0] Activity4 [x > 0] [x < 0] Activity5 Start- und Endzustand werden wie im Zustandsdiagramm dargestellt. Ereignisse werden nicht dargestellt. Transitionen können geteilt und synchronisiert werden (Parallelität): Activity1 Activity2 Activity4 Activity3 Sächs. VWA Fritzsche: Software-Engineering 3 Implementation von UML-Modellen 3.1 Von UML nach Java Applikationen und Applets Eine Java-Applikation ist ein Programm in einer virtuellen Maschinensprache (Java Virtual Machine Specification). Ein solches Programm wird interpretativ durch den Java-Interpreter java verarbeitet. Um z.B. das Programm MyApplication abzuarbeiten, ist in der Kommandozeile einzugeben: java MyApplication Der Name des auszuführenden Programmes wird dem Interpreter java als Kommandozeilenparameter übergeben. Durch diese Art der Verarbeitung wird Plattformunabhängigkeit gewährleistet, die Verfügbarkeit eines Interpreters auf jeder Plattform vorausgesetzt. Java-Applikationen besitzen eine Klassenstruktur, d.h. sie bestehen aus einer Anzahl zusammenwirkender Klassen. Ein Java-Programm in der virtuellen Maschinensprache liegt in einer Datei vor. Dateiextension ist .class. Eine .class-Datei wird durch den Java-Compiler aus einer gleichnamigen .java-Datei erzeugt, die den Programmtext enthält. Bei Aufruf des Java-Interpreters wird die Dateiextension .class weggelassen. Im obigen Beispiel ist der vollständige Dateiname MyApplication.class. Beispiel: Die Anwendung MyApplication besteht aus einer Klasse, der Hauptklasse MyApplication. Der Programmtext könnte wie folgt aussehen: public class MyApplication { public static void main (String args[]){ System.out.println("Kommandozeilenparameter:"); for (int i = args.length - 1; i>=0; i--) System.out.println(args[i]); } } Die Hauptklasse einer Anwendung (z.B. MyApplication) muß die Methode main() implementieren. Mit static gekennzeichnete Methoden oder Variablen beziehen sich auf eine Klasse (betrachtet als Objekt) und nicht auf ein Instanzobjekt der Klasse. Wir nennen sie Klassenmethoden bzw. Klassenvariablen. Für jede Methode ist ein Ergebnistyp festzulegen. Wird dieser mit void angegeben, liefert die Methode keinen Ergebniswert. Beim Programmaufruf können Kommandozeilen-Parameter an eine Applikation übergeben werden. Im Beispiel kann über das String-Array args in der main-Methode auf solche Parameter zugegriffen werden. Zur Verwaltung seiner Länge (Anzahl seiner Elemente) besitzt jedes Array das Feld length. Auf dieses Feld wird wie auf eine Instanzvariable eines Objektes zugegriffen. Zeichenketten (Typ String) sind immer Objekte, also keine Character-Arrays. Die Klasse String wird in einer Klassenbibliothek bereitgestellt. Bei der Erzeugung von String-Objekten kann eine Initialisierung mittels String-Literalen vorgenommen werden: String s = new String("Kommandozeilenparameter:") 18 Sächs. VWA Fritzsche: Software-Engineering int, float, char, boolean bezeichnen elementare Datentypen (keine Klassen). Zu jedem elementaren Datentyp gibt es eine korrespondierende Klasse. Java-Programme können auf die Standard-Ein-/Ausgabe zugreifen. System ist eine abstrakte Klasse. System.in ist eine Variable für die Standard-Eingabe. Analog ist System.out eine Variable für die Standard-Ausgabe. Beispiel für einen Aufruf: java MyApplication huhu haha hoho Kommandozeilenparameter: hoho haha huhu Applets Applets sind Java-Anwendungen, die in HTML-Dokumente mittels eines AppletTags eingebunden werden können. Die gängigen Browser verfügen über eine JVM zur Interpretation solcher Java-Anwendungen. Beispiel: <html> <head> <title>Applet-Demonstration</title> <!-- (c)Hartmut Fritzsche, 20-Sept-1998--> </head> <body background=back_ms.gif text=#0000cc> <h1>Lehrveranstaltung "Software Engineering"</h1> <applet codebase="file:/home/fritzsch/java/SE_LV" code="ChboxDemo.class" alt="Applet!!!" width=500 height=300 align=bottom> The Browser doesn't know the applet-tag. </applet> </body> </html> Ein Browser, der das Applet-Tag nicht kennt, überliest es. Alternative HTML-Elemente können in die Applet-Umgebung eingefügt werden. Kennt ein Browser das Applet-Tag im obigen Beispiel nicht, erscheint stattdessen The Browser doesn't know the applet-tag. Das Attribut ALT wird verwendet, um bei Textbrowsern anstelle des Applets alternativen Text zu zeigen. Mit dem Attribut CODE wird die Datei spezifiziert, in der die Hauptklasse des Applets liegt. Jede Klasse, die ein Applet realisiert, muss von der Klasse Applet abgeleitet sein, d.h. von dieser Klasse erben (extends …). Jedes Applet besitzt die Methoden init(), start(), stop() und destroy(), die standardmäßig keinen Code enthalten. Die Methoden stehen im Zusammenhang mit dem Lebenszyklus eines Applets. Beispiel: import java.applet.Applet; import java.awt.*; 19 Sächs. VWA Fritzsche: Software-Engineering public class AppletDemo extends Applet{ String text; public void init(){ System.out.println("init"); if ((text = getParameter("text")) == null) text = "kein text-Parameter vorhanden"; System.out.println(text); } } public void start(){ System.out.println("start"); } public void stop(){ System.out.println("stop"); } public void destroy(){ System.out.println("destroy"); } public void paint(Graphics g){ g.drawString("Hello world !",90,25); } Ein Browser gibt Ausgaben auf der Standardausgabe über die "Java-Console" aus. Ist das Applet während der Präsentation geändert worden, d.h es ist ein neues .class-File erzeugt worden, muss der Applet-Viewer neu gestartet werden, um das geänderte Applet zu präsentieren. 3.2 Ein Beispiel: Baum-Klassen Als Beispiel sollen die im Abschnitt über Polymorphie betrachteten und modellierten Unruhen nun implementiert werden. Getreu dem Klassenmodell programmieren wir die Klassen Mobile, MobileA und Node. public class Mobile{ Node root = new Node(); Mobile lsucc,rsucc; void insKeyWeight(int key,int weight){ node.setKey(key); node.setWeight(weight); } void insLeft(Mobile s){ lsucc = s; } void insRight(Mobile s){ rsucc = s; } int weight(){ if (node.getWeight() == 0) return( lsucc.weight() + rsucc.weight() ); else return( node.getWeight()); 20 Sächs. VWA Fritzsche: Software-Engineering } boolean balancedp(){ if (node.getWeight() == 0) return( lsucc.balancedp() && rsucc.balancedp() && (lsucc.weight() == rsucc.weight())); else return(true); } void makebalance(){ } } class MobileA extends Mobile{ private int larm,rarm; void setLarm(int a){ larm = a; } void setRarm(int a){ rarm = a; } boolean balancedp(){ if (node.selWeight() == 0) return( lsucc.balancedp() && rsucc.balancedp() && (larm * lsucc.weight() == rarm * rsucc.weight())); else return(true); } public void makebalance(){ int s = larm + rarm; if (root.selWeight() == 0){ ((MobileA)lsucc).makebalance(); ((MobileA)rsucc).makebalance(); if (!(this.balancedp())){ rarm = lsucc.weight()*s/this.weight(); larm = s - rarm;} System.out.println(larm); System.out.println(rarm); } } } class Node{ private int key,weight; int getWeight(){ return weight; } int getKey(){ return key; } void setWeight(int weight){ this.weight = weight; } void setKey(int pkey){ key = pkey; 21 Sächs. VWA Fritzsche: Software-Engineering } } Die Klasse UseMobile bietet eine beispielhafte Anwendung: public class UseMobile { public static void main(String args[]){ System.out.println("Hello world!"); Mobile m1 = new Mobile(); m1.insKeyWeight(1,0); Mobile m2 = new Mobile(); m2.insKeyWeight(2,4); Mobile m3 = new Mobile(); m3.insKeyWeight(3,0); Mobile m4 = new Mobile(); m4.insKeyWeight(4,0); Mobile m5 = new Mobile(); m5.insKeyWeight(5,1); Mobile m6 = new Mobile(); m6.insKeyWeight(6,1); Mobile m7 = new Mobile(); m7.insKeyWeight(7,2); m4.insLsucc(m5);m4.insRsucc(m6); m3.insLsucc(m4);m3.insRsucc(m7); m1.insLsucc(m2);m1.insRsucc(m3); System.out.println(m1.weight()); System.out.println(m1.balancedp()); MobileA ma1 = new MobileA(); ma1.insKeyWeight(1,0); MobileA ma2 = new MobileA(); ma2.insKeyWeight(2,3); MobileA ma3 = new MobileA(); ma3.insKeyWeight(3,0); MobileA ma4 = new MobileA(); ma4.insKeyWeight(4,0); MobileA ma5 = new MobileA(); ma5.insKeyWeight(5,2); MobileA ma6 = new MobileA(); ma6.insKeyWeight(6,1); MobileA ma7 = new MobileA(); ma7.insKeyWeight(7,1); ma4.insLeft(ma5);ma4.insRight(ma6); 22 Sächs. VWA Fritzsche: Software-Engineering ma4.insLarm(1);ma4.insRarm(2); ma3.insLeft(ma4);ma3.insRight(ma7); ma3.insLarm(1);ma3.insRarm(3); ma1.insLeft(ma2);ma1.insRight(ma3); ma1.insLarm(4);ma1.insRarm(3); System.out.println(ma1.weight()); System.out.println(ma1.balancedp()); } ma4.insLarm(4); System.out.println(ma1.weight()); System.out.println(ma1.balancedp()); ma1.makebalance(); System.out.println(ma1.weight()); System.out.println(ma1.balancedp()); System.out.println("Bye world!"); } Abarbeitung des (zuvor übersetzten) Programmes: C:\WorkStation\SE>java UseMobile Hello world! 8 true 7 true 7 false 2 4 1 3 4 3 7 true Bye world! 3.3 Interfaces und Mehrfachvererbung In Java gibt es (z.B. im Unterschied zu C++) keine Mehrfachvererbung. Statt dessen gibt es sog. Interfaces, die aber reine Schnittstellen darstellen und keinerlei Implementierung enthalten. Interfaces definieren ausschließlich abstrakte Methoden und Konstanten. Eine Klasse kann ein oder mehrere Interfaces implementieren. Wenn eine Klasse ein Interface implementiert, dann muss sie alle seine Methoden überschreiben. Die Eigenschaft einer Klasse, ein Interface zu implementieren, wird an ihre Nachfahren vererbt. 23 Sächs. VWA Fritzsche: Software-Engineering 3.4 Implementation von Assoziationen und Aggregationen Zur Implementation von Assoziationen und Aggregationen mit 1:n – Beziehungen eignen sich Vektoren und Hash-Tabellen. Ein Vektor ist in Java (als Instanz der Klasse Vector) ein eindimensionales dynamisches Array. „Dynamisch“ bedeutet hier, dass die Anzahl der Elemente zur Laufzeit des Programms vergrößert werden kann. Der Klass Vector zugeordnete Methoden sind addElement, insertElement, removeElement. Die Methode size liefert als Wert die Anzahl der Elemente eines Vektors. Hash-Tabellen bieten den statistisch gesehen schnellsten Zugriff auf Elemente einer Elementsammlung und werden deshalb häufig eingesetzt. In ca. 70 – 90 % aller Fälle wird ein Direktzugriff möglich sein. In Java ist eine entsprechende Klasse Hastable verfügbar. 3.5 Dynamische Modelle und Event-Handling Ziel ist hier die Umsetzung von Zustandsdiagrammen in Java-Programme. Bei jeder Mausbetätigung (auch Mausbewegung!) oder Tastatureingabe wird ein Objekt der Klasse Event bzw. einer Subklasse dieser Klasse erzeugt. Den Programmkomponenten, die auf Events reagieren sollen, werden zur jeweiligen Event-Art passende Listener hinzugefügt. Die Programmierung der gewünschten Aktionen bei Eintreten bestimmter Ereignisse erfolgt in sog. Handlern. Listener-Interfaces definieren solche Handler zur Behandlung von Events. Ein Listener kann mehrere Event-Quellen haben. Zu einer EventQuelle kann es andererseits auch mehrere Listener geben. import java.applet.*; import java.awt.event.*; import java.awt.*; 24 Sächs. VWA Fritzsche: Software-Engineering 25 public class AcEvDemo2 extends Applet implements ActionListener { public void init() { System.out.println("Hello world!"); Button bopen = new Button("open"); Button bclose = new Button("close"); bopen.addActionListener(this); bclose.addActionListener(this); add(bopen); add(bclose); System.out.println("I am waiting for events."); } public void actionPerformed(ActionEvent evt) { if (evt.getActionCommand().equals("open")){ System.out.println("action in open"); } if (evt.getActionCommand().equals("close")){ System.out.println("action in close"); } } } 4 Systemarchitekturen 4.1 Komponentenmodelle Betrachtet man die logische Struktur eines objektbasierten Systems, so sieht man Klassenstrukturen. Bei der Systemarchitektur wird dagegen häufig eine nicht objektbasierte Komponentenstruktur zugrunde gelegt. Zur Darstellung der Komponentenstruktur werden Komponentenmodelle verwendet. Komponentenmodelle werden in der UML mit Komponentendiagrammen repräsentiert. Eine Komponente stellt eine physikalische Programmeinheit dar, die als Quellcode, Objektcode oder ausführbares Programm vorliegen kann. Komponenten werden in Subsystemen (Paketen) organisiert. Pakete (packages) werden zur Gruppierung von Modellelementen verwendet. In Java umfasst ein Paket eine beliebige Anzahl von Compilationseinheiten. Pakete können hierarchisch gegliedert sein. Dabei korrespondieren Paketnamen mit Pfad-/Dateinamen. Darstellung : Subsystem1 Component1 Component2 Sächs. VWA Fritzsche: Software-Engineering 26 4.2 Verteilungsmodelle Das Verteilungsdiagramm ermöglicht die Modellierung expliziter physischer Strukturen. Es enthält Knoten, auf denen Prozesse ablaufen. Knoten werden durch Quader dargestellt. Client1 Server1 Component1 Client2 Component2 4.3 Objektserialisierung Die Objektserialisierung dient der persistenten Speicherung von Laufzeitobjekten einer Java-Applikation zu einem bestimmten Zeitpunkt. Das Speichern in eine Datei wird als „Serialisieren“ bezeichnet. Im Paket java.io existiert eine Klasse ObjectOutputStream, auf die die Methode writeObject anzuwenden ist. Zu serialisierende Objekte sind dieser Methode als Parameter zu übergeben. Das Wiederherstellen einer Objektwelt durch Lesen aus der zuvor serialisierten Objektwelt wird als „Deserialisieren“ bezeichnet. Dazu ist die Methode readObject der Klasse ObjectInputStream anzuwenden. 5 Der Softwareentwicklungsprozess 5.1 Anwendungsfall-Analyse Ein Anwendungsfall (use case) beschreibt die Interaktionen zwischen Anwendern und dem Anwendungssystem, die notwendig sind, um einen Arbeitsgang durchzuführen. Im Wesentlichen sind Anwendungsfälle und Akteure zu identifizieren. Das Darstellungsmittel der UML für Anwendungsfälle sind UseCase-Diagramme. Ein UseCase-Diagramm zeigt die Beziehungen zwischen Akteuren (actors) und Anwendungsfällen in einem System. Das System wird durch ein Rechteck dargestellt, das die Systemgrenzen zeigt. Im UseCase-Diagramm können auch Import-Beziehungen (<<include>>) und Erweiterungen (<<extend>>) zwischen Anwendungsfällen gezeigt werden. Auch Generalisierung/Spezialisierung zwischen Anwendungsfällen kann modelliert werden. Sächs. VWA Fritzsche: Software-Engineering 27 5.2 Methodische Aspekte der Modellierung In der Analysephase sind folgende Aktivitäten erforderlich: • Zerlegung des Anwendungsbereiches in Unterbereiche • Analyse und Spezifikation des geforderten Systemverhaltens Während der Problembereichsanalyse werden zunächst „Geschäftsklassen“ modelliert. „Fachklassen“ beschreiben im Unterschied dazu implementierungs-technische Sachverhalte. Zur Entwicklung eines Objektmodells wird von RUMBAUGH folgendes Vorgehen empfohlen: 1. Objektklassen identifizieren 2. ein Data Dictionary vorbereiten 3. Assoziationen zwischen Objektklassen identifizieren 4. Attribute identifizieren und zu Objektklassen hinzufügen (Operationen erst spät beim Spezifizieren des Zustandsmodells hinzufügen) 5. Klassen mittels Vererbung organisieren 6. Zugriffspfade testen 7. das Gesamtmodell in einem iterativen Prozess verfeinern 8. Klassen zu Paketen (Teilsystemen) gruppieren 5.3 Qualitätssicherung Die Qualitätssicherung umfasst Planung/Durchführung von Qualitätssicherungsmaßnahmen (QSM) , die Kontrolle der Einhaltung von Standards (ISO 9000, ... ) und die Qualitätsbewertung anhand von Metriken. Es werden konstruktive und analytische QSM unterschieden. Zu den analytischen QSM zählen statische Analysen, Sächs. VWA Fritzsche: Software-Engineering der symbolische Test und dynamische Analysen (das „Testen“). Zu den statischen Analysen zählen Kontrollfluss- und Datenflussanalyse sowie die Programmverifikation (Beweis der Korrektheit eines Programms mit mathematischen Mitteln). 5.4 Projektmanagement und Prozessmodellierung Das Projektmanagement (PM) umfasst alle Maßnahmen zur Planung und Verfolgung einzelner Projekte und von Projektfamilien. Zur Projektplanung zählen: • Festlegung der Projektorganisation • Personalplanung • Meilenstein-/Terminplanung für alle Aktivitäten und Zuordnung zu Bearbeitern. Die Projektverfolgung umfasst alle technisch-organisatorischen Maßnahmen zur Erreichung der Projektziele. Dazu zählen die Verfolgung von Meilensteinen, Terminen und (Rest-) Aufwänden sowie die Kontrolle der Aktivitäten der Bearbeiter. Grundlage der Projektmanagement-Aktivitäten bilden Dokumente spezieller Typen, z.B. Aktivitätengraphen, Balkendiagramme usw. Gegenstand der Prozess-modellierung ist die Formalisierung und Computerunterstützung des Software-entwicklungsprozesses selbst. Neben Bausteinen und Ergebnissen müssen Aufgaben und Ressourcen verwaltet werden. Wichtig ist dabei die Konsistenzsicherung. 5.5 Konfigurationsmanagement Das Konfigurationsmanagement umfasst Aufgaben wie • • • die Kontrolle der entwickelten Quellprogramme die Bereitstellung von „build“-Funktionalität das Release-Engineering Das Concurrent Versions System (CVS) ist ein Versionsverwaltungssystem, das das Versionsmanagement auf der Ebene von Quellprogrammen unterstützt. Es ermöglicht die Aufzeichnung der Entwicklungsgeschichte von Programmdokumenten in Projekten und unterstützt die Gruppenarbeit (check out –check in). Neuere Versionsverwaltungssysteme sind Subversion und Mercurial. In Eclipse wird Versionsverwaltung durch spezielle Plug-ins unterstützt (z.B. Mercurial: http://www.javaforge.com/project/HGE). Weitere Werkzeuge wie Ant und Maven dienen der Unterstützung des Build-Prozesses. 6 Wiederverwendung 6.1 Komponentenbasierte Softwareentwicklung - Frameworks Klassen realisieren „Abstrakte Datentypen“. Sie verkörpern Objektfabriken, weil sie Operationen zum Erzeugen von Exemplaren (Konstruktoren) zur Verfügung stellen. Wiederverwendung individueller Komponenten ist nicht kostenlos. Es entsteht Aufwand für das Ausfindigmachen nachnutzbarer Komponenten. OO-Sprachen allein garantieren keine Verbesserung der Wiederverwendbarkeit. 28 Sächs. VWA Fritzsche: Software-Engineering Ein Framework ist eine Sammlung verschiedener individueller Komponenten mit definiertem Kooperationsverhalten zur Lösung einer Aufgabe. Frameworks definieren in der Regel einen Großteil der Architektur. Wieder-verwendbare Architekturansätze standardisieren langfristig Anwendungsgebiete. Whitebox-Frameworks bestehen aus einer Anzahl unvollständig spezifizierter Klassen, d.h. Klassen mit abstrakten Methoden. Diese abstrakten Methoden heißen Einschubmethoden. Um ein Whitebox-Framework anzupassen, muß der Programmierer die Implementation des Frameworks weitgehend kennen. Blackbox-Frameworks gehen von einer Anzahl fertiger Komponenten aus. Anpassungen des Frameworks werden durch Kompositionen der Komponenten erreicht, nicht durch Vervollständigung von Klassen. 6.2 Entwurfsmuster Ein wesentlicher Beitrag zur Thematik der Entwurfsmuster wurde von E. Gamma geleistet. Gamma et al. haben einen Katalog mit 23 Entwurfsmustern beschrieben. Bekannte Entwurfsmuster sind das „Beobachter“-Muster, das „Kompositum“-Muster und das „Strategie“-Muster. Sie finden u.a. in der Model-View-Controler-Architektur Anwendung. Beobachter-Muster: Definiere eine 1-zu-n-Abhängigkeit zwischen Objekten, so dass die Änderung des Zustandes eines Objektes dazu führt, dass alle abhängigen Objekte benachrichtigt und automatisch aktualisiert werden. Kompositum-Muster: Füge Objekte zu Baumstrukturen zusammen, um Teil-GanzesHierarchien zu repräsentieren. Das Kompositionsmuster ermöglicht es Klienten, einzelne Objekte sowie Kompositionen von Objekten einheitlich zu behandeln. Strategie-Muster: Definiere eine Familie von Algorithmen, kapsele jeden einzelnen und mache sie austauschbar. Das Strategie-Muster ermöglicht es, den Algorithmus unabhängig von ihn nutzenden Klienten zu variieren. 6.3 Grafische Benutzeroberflächen In Java existiert eine sehr gute Unterstützung für die Entwicklung von Oberflächen durch das „Abstract Window Toolkit“ (AWT) und das Paket „Swing“. Der Aufbau der AWT-Klassenbibliothek ist ein Beispiel für die Anwendung des Entwurfsmusters „Kompositum“. Layout-Manager nehmen dem Anwender die Arbeit der absoluten Positionierung von Komponenten in Containern ab. Sie definieren Regeln, nach denen Komponenten in einem Container platziert werden. Es gibt eine Vielzahl von einfach handhabbaren bis hin zu komfortablen Layouts: BorderLayout, FlowLayout, GridLayout, GridBagLayout, usw. 6.4 Die Model-View-Controller-Architektur Das Model-View-Controller-Prinzip bietet einen Ansatz für die Architektur von Anwendungen, die aus einer grafischen Benutzeroberfläche, einem funktionalen Kern und einer Datenverwaltung bestehen sollen. Ein Model-Objekt stellt das Anwendungsobjekt dar, das View-Objekt seine Bildschirmrepräsentation und das Controller-Objekt bestimmt die Möglichkeiten, mit denen auf Benutzereingaben reagiert werden kann. View und Model werden durch den Aufbau eines Protokolls zur Benachrichtigung entkoppelt. Der Antwortmechanismus der Oberfläche wird in einem Controller gekapselt. 29 Sächs. VWA Fritzsche: Software-Engineering Die MVC-Architektur verwendet die Entwurfsmuster Beobachter und Strategie und Kompositum. Ein oder mehrere Beobachter melden sich beim Model-Objekt an und werden von diesem benachrichtigt, sobald das Model-Objekt seinen Zustand ändert. Das Model-Objekt liefert selbst keine Daten an vorhandene Beobachter, diese holen sich die Informationen vom Model-Objekt, die sie jeweils benötigen. 30