Software Engineering1
1 Architektur grafischer Benutzungsoberflächen 11
1.1 AWT 11
1.1.1 Beispiel Calculator 11
1.1.2 AWT und Swing 12
1.1.2.1 AWT 12
1.1.2.2 Swing 12
1.1.2.3 Überblick über AWT Components 13
1.1.3 Erstellen eines normalen Fensters oder eines Dialogs 13
1.1.3.1 Beispiel zum Erzeugen eines Frames 13
1.1.3.2 Aktionen zum Erstellen 14
1.1.3.2.1 Anlegen eines Dialogfensters 14
1.1.3.2.2 Zuordnen eines Layoutmanagers 14
1.1.3.2.3 Einfügen von Dialogelementen 14
1.1.3.2.4 Anzeigen des Dialogfensters 15
1.1.3.3 Beispiel Anpassen eines Frames 15
1.1.4 Eventmodell 16
1.1.4.1 Beispiel ActionEvent 16
1.1.4.2 Überblick 16
1.1.4.3 Ereignisse 17
1.1.4.3.1 Diagramm 17
1.1.4.3.2 Primitive Ereignisse 17
1.1.4.3.3 Ereignisse zur Interaktion verschiedener Komponenten 18
1.1.4.4 Listener 18
1.1.4.4.1 Konzept 18
1.1.4.4.2 Listenerdiagramm 18
1.1.4.4.3 Listener-Schnittstellen 18
1.1.4.5 Ereignisquellen 19
1.1.4.5.1 Konzept 19
1.1.4.5.2 Ereignisquellen 19
1.1.4.6 Aktivierung von Lauschobjekten 19
1.1.4.7 Beispiel MouseEventsDemoEventTechniken 20
1.1.4.7.1 Implementation Listener durch Frame 20
1.1.4.7.2 Adapter 21
1.1.4.7.3 Anonyme Klasse 21
1.1.4.8 Adapter Klassen 22
1.1.4.9 Adapter als innere Klassen 22
1.1.4.10 Beispiele 23
1.1.4.10.1 Beispiel Schließen eines Frames mit Escape 23
1.1.4.10.1.1 Implementation Keylistener 23
1.1.4.10.1.2 Innerere Klasse und Adapter 23
1.1.4.10.1.3 Anonyme Klasse 24
1.1.4.10.1.4 Überlagern der Event-Handler in den Komponenten 24
1.1.4.10.2 Key Events 26
1.1.4.10.3 Mouse Events 29
1.1.4.10.4 Component Events 29
1.1.4.10.5 Focus Events 29
1.1.5 Layoutmanager 29
1.1.5.1 Beispiel Layout 29
1.1.5.2 Konzept 30
1.1.5.3 FlowLayout 30
1.1.5.4 GridLayout 31
1.1.5.5 BorderLayout 31
1.1.5.6 CardLayout 32
1.1.5.7 GridBagLayout 32
1.1.5.8 Null-Layout 32
1.1.6 Modale Dialoge 32
1.1.6.1 Beispiel Dialoge 32
1.1.6.2 Warten auf die Eingabe 33
1.1.6.3 Beispiel VariableDialoge 33
1.2 Swing 33
1.2.1 Beispiel Geburtsmonat 33
1.2.2 Probleme des AWT 35
1.2.3 Leichtgewichtige Komponenten 35
1.2.4 Pluggable Look-and-Feel 35
1.2.5 Das Model-View-Controller-Prinzip 35
1.2.6 Container und Menüs 36
1.2.6.1 Hauptfenster 36
1.2.6.1.1 Beispiel MDI 36
1.2.6.1.2 JFrame 37
1.2.6.1.2.1 Struktur 37
1.2.6.1.2.2 Erzeugung 37
1.2.6.1.2.3 Zugriff 37
1.2.6.1.3 JInternalFrame 38
1.2.6.1.3.1 Konzept 38
1.2.6.1.3.2 Der Desktop 38
1.2.6.1.3.3 Die Kindfenster 38
1.2.6.1.4 Beispiel SplashScreen 38
1.2.6.1.5 JWindow 38
1.2.6.1.6 JDialog 39
1.2.6.1.7 Beispiel Quartal 39
1.2.6.1.8 JOptionPane 39
1.2.6.1.9 JApplet 39
1.2.6.2 Menüs 40
1.2.6.2.1 Einfache Menüs 40
1.2.6.2.1.1 Beispiel SimpleMenue 40
1.2.6.2.1.2 Grundlagen von Swing-Menüs 41
1.2.6.2.2 Weitere Möglichkeiten 41
1.2.6.2.2.1 Beispiel Extended Menue ( funktioniert nicht mit VisualCafe) 41
1.2.6.2.2.2 Untermenüs 42
1.2.6.2.2.3 Icons in Menüeinträgen 42
1.2.6.2.2.4 Checkboxes und Radiobuttons in Menüeinträgen 43
1.2.6.2.3 Kontextmenue 43
1.2.6.2.3.1 Beispiel Kontextmenue 43
1.2.6.2.3.2 Kontextmenüs 43
1.2.7 Swing Komponenten 43
1.2.7.1 Einfache Swing Komponenten 43
1.2.7.1.1 Beispiel Swing Components 43
1.2.7.1.2 Label und Textfelder 48
1.2.7.1.2.1 JLabel 48
1.2.7.1.2.2 JTextField 48
1.2.7.1.2.3 JPasswordField 48
1.2.7.1.2.4 JTextArea 49
1.2.7.1.3 Buttons 49
1.2.7.1.3.1 JButton 49
1.2.7.1.3.2 JCheckBox 49
1.2.7.1.3.3 JRadioButton 49
1.2.7.1.4 Listen und Comboboxen 49
1.2.7.1.4.1 JList 50
1.2.7.1.4.2 JComboBox 50
1.2.7.1.5 Quasi-analoge Komponenten 50
1.2.7.1.5.1 JScrollBar 50
1.2.7.1.5.2 JSlider 51
1.2.7.2 Jtree 52
1.2.7.2.1 Beispiel SelectionTree 52
1.2.7.2.2 Bäume 53
1.2.7.2.3 Interfaces für das Model-View Konzept 53
1.2.7.2.4 Default-Implementationen der Interfaces 54
1.2.7.2.5 Erzeugen eines Baumes 55
1.2.7.2.6 Selektieren von Knoten 55
1.2.7.2.7 Beispiel MutableTree 56
1.2.7.2.8 Verändern der Baumstruktur 57
1.2.7.3 JTabbedPane 58
1.2.7.3.1 Beispiel TabbedPane 58
1.2.7.3.2 JTabbedPane 59
1.3 Grafiken und Bilder 59
1.3.1 Grundlagen der Grafikausgabe 59
1.3.1.1 Beispiel PrimitiveRoutines 59
1.3.1.2 Die Methode paint 60
1.3.1.3 Die Klasse Graphics 60
1.3.1.4 Das grafische Koordinatensystem 60
1.3.1.5 Elementare Grafikroutinen 61
1.3.1.5.1 Linien 61
1.3.1.5.2 Rechteck 61
1.3.1.5.3 Polygon 61
1.3.1.5.4 Kreis 62
1.3.1.5.5 Kreisbogen 62
1.3.1.6 Linien und Füllmodus 62
1.3.1.7 Kopieren und Löschen von Flächen 62
1.3.1.8 Die Clipping-Region 63
1.3.2 Bitmaps und Animation 63
1.3.2.1 Bitmaps 63
1.3.2.1.1 Beispiel BitmapOhneRepaintPaintMechanismus 63
1.3.2.1.2 Laden einer Bitmap 63
1.3.2.1.3 Die Klasse Toolkit 64
1.3.2.1.4 Ausgeben einer Bitmap 64
1.3.2.1.5 Die Klasse MediaTracker 64
1.3.2.1.5.1 Problem 64
1.3.2.1.5.2 Lösung durch einen MediaTracker 64
1.3.2.1.5.3 Beschreibungen aus der Online Hilfe 65
1.3.2.1.6 Verbleibendes Problem 66
1.3.2.1.7 Beispiel Bitmap (Mit repaint-paint Mechanismus) 66
1.3.2.1.8 Der repaint() – paint() Mechanismus 66
1.3.2.1.8.1 Die Methode paint() 66
1.3.2.1.8.2 Die Methode repaint() 66
1.3.2.1.8.3 Die Methode update() 67
1.3.2.1.8.4 Anmerkungen 67
1.3.2.1.8.5 Unterschiede zwischen AWT und Swing 67
1.3.2.1.8.5.1 AWT 67
1.3.2.1.8.5.2 Swing 68
1.3.2.2 Animation 68
1.3.2.2.1 Beispiel BitmapAnimation 68
1.3.2.2.2 Konzept 69
1.3.2.2.3 Beispiel FlackerndesImageApplet 70
1.3.2.2.4 Reduktion des Bildschirmflackerns 70
1.3.2.2.5 Beispiel PartialRemovingImageApplet 71
1.3.2.2.6 Selektives Löschen 72
1.3.2.2.7 Beispiel DoubleBufferingImageApplet 72
1.3.2.2.8 Doppelpufferung 73
1.3.2.2.9 Beispiel ExclusiveOrImageApplet 73
1.3.2.2.10 Löschen mit Exclusiv-Oder 74
1.4 Objektserialiserung 74
1.4.1 Serialisierung von einfachen Objekten 74
1.4.1.1 Beispiel Einfache Serialisierung 74
1.4.1.2 Begriffsbestimmung 75
1.4.1.3 Schreiben von Objekten 75
1.4.1.3.1 Die Klasse ObjectOutputStream 75
1.4.1.3.2 Methoden von ObjectOutputStream 75
1.4.1.3.3 Konzept 75
1.4.1.3.4 Das Interface Serializable 76
1.4.1.4 Lesen von Objekten 76
1.4.1.5 Anmerkungen 76
1.4.1.6 Auszug aus der Online Hilfe 77
1.4.1.7 Nicht-serialisierte Membervariablen 77
1.4.2 Serialiserung von Graphen 78
1.4.2.1 Beispiel Graph Serialiserung Familie 78
1.4.2.2 Objektreferenzen 79
1.4.2.2.1 Problem 79
1.4.2.2.2 Lösung 79
1.4.2.2.3 Warnungen 79
1.4.3 Serialisierung von Collections 80
1.4.3.1 Beispiel ObjectStore 80
1.4.3.2 Serialisierbare Standardklassen 81
1.4.4 Kopieren durch Serialisieren 81
1.4.4.1 Beispiel Kopieren durch Serialisierung 82
1.4.4.2 Serialisieren in Byte Array 83
1.5 Exception Handling 83
1.5.1 Behandlung von Exceptions 83
1.5.1.1 Beispiel Simple Exception 83
1.5.1.2 Die try-catch-Anweisung 84
1.5.1.3 Das Fehlerobjekt 84
1.5.1.4 Die Fehlerklassen von Java 84
1.5.1.5 Fortfahren nach Fehlern 85
1.5.1.6 Beispiel MultipleCatchException 86
1.5.1.7 Mehrere catch-Klauseln 86
1.5.1.8 Die finally-Klausel 86
1.5.2 Weitergabe von Exceptions 87
1.5.2.1 Beispiel Divide 87
1.5.2.2 Die catch-or-throw-Regel 88
1.5.2.3 Auslösen von Ausnahmen 88
1.6 Globalisierung 88
1.6.1 Problem 88
1.6.2 Beispiel LocalizedClock 88
1.6.3 Die Klasse Locale 89
1.6.4 Die Klasse DataFormat 90
1.6.5 Beispiel SimpleGlobalMenue 90
1.6.6 Die Klasse ResourceBundle 91
2 Die Unified Modeling Language (UML) und der Rational Unified Software Development Process (RUP) 91
2.1 UML im Überblick 91
2.1.1 Dokumentation 91
2.1.1.1 Wozu dient die Dokumentation 91
2.1.1.2 Anmerkungen zur Dokumentation 91
2.1.2 Geschichte der UML 92
2.1.3 Views 92
2.2 RUP im Überblick 93
2.3 Aktivitäten des RUP und die zugehörigen UML Diagramme 93
2.3.1 Business Modeling 93
2.3.2 Requirements 94
2.3.2.1 Erstellen einer Vision 94
2.3.2.1.1 Beispiel Getränkeautomat 94
2.3.2.1.2 Beispiel Bibliothek 94
2.3.2.1.3 Konzept 94
2.3.2.2 Aufnehmen der Stakeholder Requests 94
2.3.2.3 Erstellen eines Use Case Models 95
2.3.2.3.1 Beispiel Getränkeautomat 95
2.3.2.3.2 Beispiel Bibliothek 95
2.3.2.3.3 Überblick 96
2.3.2.3.4 System 96
2.3.2.3.5 Actor 96
2.3.2.3.5.1 Charakterisierung 96
2.3.2.3.5.2 Beziehungen zwischen Actors 97
2.3.2.3.6 Use Case 97
2.3.2.3.6.1 Charakterisierung 97
2.3.2.3.6.2 Spezifikation 97
2.3.2.3.6.3 Beziehungen zwischen Use Cases 98
2.3.2.3.7 Erweiterung der UML 98
2.3.2.3.8 Wie findet man Actors und Use Cases 98
2.3.2.4 Erstellen von Prototypen für User Interfaces 99
2.3.2.4.1 Beispiel Bibliothek 99
2.3.2.4.2 Getränkeautomat 100
2.3.2.5 Festlegen der Supplementary Requirements 100
2.3.2.5.1 Beispiel Bibliothek 100
2.3.2.5.2 Beispiel Getränkeautomat 100
2.3.2.6 Festlegen der Supplementary Requirements 101
2.3.2.6.1 Beispiel Bibliothek 101
2.3.2.6.2 Beispiel Getränkeautomat 101
2.3.2.6.3 Fragenkatalog für Supplementary Requirements (aus Industrieprojekt für Bildverarbeitung) 101
2.3.2.6.3.1 Auslieferungstermine/Zeitresourcen 101
2.3.2.6.3.2 Kostengrenze/Budget/Geldresourcen 101
2.3.2.6.3.3 Nationale oder Internationale Regularien 101
2.3.2.6.3.4 Conditions 101
2.3.2.6.3.5 Produktsupport 101
2.3.2.6.3.6 Juristische Anforderungen 101
2.3.2.6.3.7 Technologische Randbedingungen für die Lösung (Solution Constrains) 101
2.3.2.6.3.8 Anforderungen bzgl. Datenvolumen 102
2.3.2.6.3.9 Zeitanforderungen/Performance 102
2.3.2.6.3.10 Anforderungen an die Präzision des Systems 102
2.3.2.6.3.11 Lastanforderungen 102
2.3.2.6.3.12 Anforderungen an Fehlertoleranz, Robustheit des Systems 102
2.3.2.6.3.13 Anforderungen an die Benutzbarkeit 102
2.3.2.6.3.14 Anforderungen an die technische Sicherheit bzgl. Anwender und collaborierende Hardware 102
2.3.2.6.3.15 Anforderungen an die Datensicherheit 102
2.3.2.6.3.16 Anforderungen an die Interoperabilität mit anderen Systemen/ Installationsumgebung (Implementation Environment) 102
2.3.2.6.3.17 Anforderungen an die Dokumentation (Benutzer+Wartung) 103
2.3.2.6.3.18 Anforderungen an die Wartbarkeit 103
2.3.2.6.3.19 Anforderungen an die Portabilität 103
2.3.2.7 Erstellen einer Liste für mögliche Anforderungen 103
2.3.2.7.1 Beispiel Bibliothek 103
2.3.2.7.2 Beispiel Getränkeautomat 103
2.3.2.8 Erstellen eines Glossary 103
2.3.2.8.1 Beispiel Bibliothek 103
2.3.2.8.2 Beispiel Getränkeautomat 103
2.3.2.9 Beispiel Bankautomat 104
2.3.3 Analyse & Design 104
2.3.3.1 Identifizierung von Klassen und Objekten 104
2.3.3.1.1 Beispiel Bibliothek 104
2.3.3.1.2 Beispiel Getränkeautomat 104
2.3.3.1.3 Grundlegende Probleme 105
2.3.3.1.4 Studieren des Problem Domains 105
2.3.3.1.4.1 Entity Class 106
2.3.3.1.4.2 Boundary Class 106
2.3.3.1.4.3 Control Class 106
2.3.3.1.5 Studium der sprachlichen Beschreibung des Problems 106
2.3.3.1.6 Studium existierender Software 107
2.3.3.2 Erstellen eines Class Diagrams 107
2.3.3.2.1 Beispiel Getränkeautomat 107
2.3.3.2.2 Beispiel Bibliothek 107
2.3.3.2.3 Ziel 107
2.3.3.2.4 UML Darstellung der Klassen 107
2.3.3.2.5 Identifizieren der Beziehungen zwischen Klassen 108
2.3.3.2.5.1 Association 108
2.3.3.2.5.1.1 Allgemein 108
2.3.3.2.5.1.2 Aggregation 108
2.3.3.2.5.1.3 Composition Aggregation 109
2.3.3.2.5.2 Realization 109
2.3.3.2.5.3 Generalization 109
2.3.3.2.5.4 Dependency 109
2.3.3.2.6 Templates 110
2.3.3.3 Weitere Beispiele 110
2.3.3.4 Erstellen der Use Case Realisierungen 112
2.3.3.4.1 Beispiel Getränkeautomat 112
2.3.3.4.2 Beispiel Bibliothek 112
2.3.3.4.3 Erstellen eines Use Case spezifischen Class Diagrams 112
2.3.3.4.4 Erstellen eines Object Diagrams 112
2.3.3.4.4.1 Konzept 112
2.3.3.4.4.2 Associations 112
2.3.3.4.5 Erstellen eines Collaboration Diagram 113
2.3.3.4.6 Erstellen eines Sequence Diagrams 113
2.3.3.5 Spezifizierung der Klassen 114
2.3.3.5.1 Beispiel Getränkeautomat 114
2.3.3.5.2 Beispiel Bibliothek 114
2.3.3.5.3 Identifizieren von Elementfunktionen und Attributen 114
2.3.3.5.4 Beschreibung der Verantwortlichkeiten der Klassen 116
2.3.3.5.5 Erstellen der Beschreibungen der Schnittstellenfunktion 116
2.3.3.5.6 Updaten der Beziehungen zwischen den Klassen 117
2.3.3.5.7 Statechart Diagram 117
2.3.3.5.7.1 Beispiel Getränkeautomat 117
2.3.3.5.7.2 Beispiel Bibliothek 117
2.3.3.5.7.3 Konzept 117
2.3.3.5.7.4 States 117
2.3.3.5.7.5 Events 118
2.3.3.5.7.6 Transitions 118
2.3.3.5.8 Activity Diagram 118
2.3.3.5.8.1 Beispiel Getränkeautomat 119
2.3.3.5.8.2 Konzept 119
2.3.3.5.9 Identifizierung aktiver Klassen 119
2.3.3.6 Erstellen eines Package Diagrams 119
2.3.3.6.1 Beispiel Getränkeautomat 119
2.3.3.6.2 Beispiel Bibliothek 120
2.3.3.6.3 Packages 120
2.3.3.6.4 Beziehungen zwischen Packages 121
2.3.3.6.5 Zugriffssteuerung 121
2.3.3.7 Erstellen des Deployment Diagrams 121
2.3.3.7.1 Beispiel Getränkeautomat 121
2.3.3.7.2 Beispiel Bibliothek 122
2.3.3.7.3 Konzept 123
2.3.3.8 Unterschied zwischen Analyse und Design 123
2.3.3.8.1 Analyse 123
2.3.3.8.2 Design 124
2.3.3.9 Weitere Beispiele 124
2.3.4 Implementation 128
2.3.4.1 Erstellen des Component Diagrams 128
2.3.4.1.1 Beispiel Getränkeautomat 128
2.3.4.1.2 Beispiel Bibliothek 128
2.3.4.1.3 Konzept 128
2.3.4.2 Erstellen des Integration Build Plan 129
2.3.4.2.1 Beispiel Getraenkeautomat 129
2.3.4.2.2 Bottom Up Integration 129
2.3.4.3 Implementieren der Components 131
2.3.4.3.1 Ziel 131
2.3.4.3.2 Programmierregeln 131
2.3.4.3.2.1 Java Code Conventions (Selbststudium) 131
2.3.4.3.3 Round Trip Engineering 146
2.3.4.3.3.1 Beispiel Bank 146
2.3.4.3.3.2 Beispiel Wertpapier 146
2.3.4.3.3.3 Sinn und Zweck 148
2.3.4.3.3.4 Tutorial für Round Trip Engineering mit Rational Rose 148
2.3.4.3.3.4.1 Grundeinstellungen 148
2.3.4.3.3.4.1.1 Setzen des CLASSPATH 148
2.3.4.3.3.4.1.2 Anlegen eines Projektverzeichnisses 148
2.3.4.3.3.4.1.3 Starten von Rose 148
2.3.4.3.3.4.1.4 Hinzufügen des Projektverzeichnisses zum CLASSPATH 149
2.3.4.3.3.4.1.5 Speichern des Rose Projektes 149
2.3.4.3.3.4.2 Vorgehensweise am Beispiel HelloWorld 149
2.3.4.3.3.4.2.1 LogicalView: Erzeugen der Start Klasse 149
2.3.4.3.3.4.2.2 Erzeugen des Codes der Klasse 149
2.3.4.3.3.4.2.3 Bearbeiten des Codes 150
2.3.4.3.3.4.2.4 ReverseEngineering der HelloWorld Klasse 150
2.3.4.3.3.4.2.5 Erzeugen von HelloWorldGUI 151
2.3.4.3.3.4.2.6 Erzeugen des Codes der Klasse 152
2.3.4.3.3.4.2.7 Bearbeiten des Codes 152
2.3.4.3.3.4.2.8 ReverseEngineering der HelloWorldGUI Klasse 152
2.3.4.4 Integrieren des Systems 153
2.3.5 Test 153
2.3.5.1 Beispiel Klassentest MyMath 153
2.3.5.1.1 BlackBoxTest 153
2.3.5.1.1.1 Testling Interface 153
2.3.5.1.1.2 Testapplikation 153
2.3.5.1.1.3 Tracer 154
2.3.5.1.1.4 TestController 155
2.3.5.1.1.5 Testprozedur 156
2.3.5.1.1.6 Einige Testfälle 156
2.3.5.1.1.6.1 Test calcDurch() 156
2.3.5.1.1.6.1.1 Testfall Standarddivision 156
2.3.5.1.1.6.1.1.1 Voraussetzungen 156
2.3.5.1.1.6.1.1.2 Eingabe 156
2.3.5.1.1.6.1.1.3 Erwartete Ausgabe 157
2.3.5.1.1.6.1.1.4 Ausgabe 157
2.3.5.1.1.6.1.2 Testfall Division durch negative Zahl 157
2.3.5.1.1.6.1.2.1 Voraussetzungen 157
2.3.5.1.1.6.1.2.2 Eingabe 157
2.3.5.1.1.6.1.2.3 Erwartete Ausgabe 157
2.3.5.1.1.6.1.2.4 Ausgabe 157
2.3.5.1.1.6.1.3 Testfall Division durch Null 157
2.3.5.1.1.6.1.3.1 Voraussetzungen 157
2.3.5.1.1.6.1.3.2 Eingabe 157
2.3.5.1.1.6.1.3.3 Erwartete Ausgabe 157
2.3.5.1.1.6.1.3.4 Ausgabe 157
2.3.5.1.1.6.2 Test calcPotenz() 158
2.3.5.1.1.6.2.1 Testfall Standard 158
2.3.5.1.1.6.2.1.1 Voraussetzungen 158
2.3.5.1.1.6.2.1.2 Eingabe 158
2.3.5.1.1.6.2.1.3 Erwartete Ausgabe 158
2.3.5.1.1.6.2.1.4 Ausgabe 158
2.3.5.1.1.6.2.2 Testfall Negativer Exponent 158
2.3.5.1.1.6.2.2.1 Voraussetzungen 158
2.3.5.1.1.6.2.2.2 Eingabe 158
2.3.5.1.1.6.2.2.3 Erwartete Ausgabe 158
2.3.5.1.1.6.2.2.4 Ausgabe 158
2.3.5.1.1.6.2.3 Testfall Exponent Null 158
2.3.5.1.1.6.2.3.1 Voraussetzungen 159
2.3.5.1.1.6.2.3.2 Eingabe 159
2.3.5.1.1.6.2.3.3 Erwartete Ausgabe 159
2.3.5.1.1.6.2.3.4 Ausgabe 159
2.3.5.1.2 WhiteBoxTest 159
2.3.5.1.2.1 Testling 159
2.3.5.1.2.2 Test Log 160
2.3.5.1.2.3 Kontrollflussgraph 162
2.3.5.2 Beispiel für Test Guidelines 163
2.3.5.2.1 Klassentest 163
2.3.5.2.1.1 Ziel 163
2.3.5.2.1.2 Zu testende Klassen 163
2.3.5.2.1.3 Black Box Test (Funktionaler Test) 163
2.3.5.2.1.3.1 Ziel 163
2.3.5.2.1.3.2 Definition der Testfälle 163
2.3.5.2.1.3.2.1 Allgemeine Hinweise 163
2.3.5.2.1.3.2.2 Äquivalenzklassenbildung 163
2.3.5.2.1.3.2.3 Grenzwertanalyse 164
2.3.5.2.1.3.2.4 Test spezieller Werte (special values testing) 165
2.3.5.2.1.3.2.5 Zufallstest 165
2.3.5.2.1.3.2.6 Ableitung von Testfällen aus State Charts 165
2.3.5.2.1.4 White Box Test (Strukturtest, Glass Box Test) 165
2.3.5.2.1.4.1 Ziel 165
2.3.5.2.1.4.2 Definition der Testfälle beim Kontrollflussorientierte Strukturtestverfahren 165
2.3.5.2.1.4.2.1 Anweisungsüberdeckung 165
2.3.5.2.1.4.2.2 Zweigüberdeckung 166
2.3.5.2.1.4.2.3 Pfadüberdeckung 166
2.3.5.2.1.4.2.4 Bedingungsüberdeckung 166
2.3.5.2.2 Integrationstest 167
2.3.5.2.2.1 Ziel 167
2.3.5.2.2.2 Für welche Gruppen von Klassen ist ein Integrationstest durchzuführen 167
2.3.5.2.2.3 Definition der Testfälle 167
2.3.5.2.3 Regressionstest 167
2.3.5.2.4 Systemtest 167
2.3.5.2.4.1 Ziel 167
2.3.5.2.4.2 Definition der Testfälle 167
2.3.5.2.5 Abnahmetest (Acceptance Test) 168
2.3.5.2.6 Testdurchführung 168
2.3.5.2.6.1 Testumgebung 168
2.3.5.2.6.2 Durchführung eines kombinierten White und Black Box Test für Klassen 169
2.3.5.2.6.3 Durchführung eines Integrationstests für eine Gruppe von Klassen 169
2.3.5.2.6.4 Wer führt den Test durch 170
2.3.5.2.6.5 Testendekriterium 170
2.3.5.2.6.6 Verwaltung der gefundenen Fehler 170
2.3.5.2.6.7 Verwaltung der Testumgebung und Testdaten 170
2.3.5.3 Erstellen eines Test Case 170
2.3.5.4 Erstellen einer Test Prozedur 171
2.3.5.5 Erstellen eines Test Log 171
2.3.5.6 Erstellen einer Test Suite (vgl. JUnit) 171
2.3.5.7 Unit Tests mit dem Test-Tool JUnit 171
2.3.5.7.1 Sinn und Zweck 171
2.3.5.7.2 Download und Installation (Selbststudium) 171
2.3.5.7.3 Ausführung der Tests mittels TestRunner 172
2.3.5.7.4 Wichtige Klassen des Frameworks 173
2.3.5.7.5 SingleTestCase 173
2.3.5.7.5.1 Beispiel EuroTestSingleTestCase 173
2.3.5.7.5.1.1 Requirements 173
2.3.5.7.5.1.2 Schreiben des Testfalles in Form der Testmethode 173
2.3.5.7.5.1.3 Schreiben der zu testenden Methode 174
2.3.5.7.5.2 Assert 174
2.3.5.7.5.3 AssertionFailedError 175
2.3.5.7.6 MultipleTestCases 176
2.3.5.7.6.1 Beispiel EuroTestMultipleTestCases 176
2.3.5.7.6.2 Definition von mehreren Testfällen in der Klasse EuroTest 176
2.3.5.7.6.3 Generierung von EuroTest Instanzen für jeden Testfall 177
2.3.5.7.7 TestCaseTree 178
2.3.5.7.7.1 Beispiel AllTests 178
2.3.5.7.7.2 TestSuite 178
2.3.5.7.8 Exceptions 178
2.3.5.7.8.1 Testen von Exceptions 179
2.3.5.7.8.2 Unerwartete Exceptions 179
2.3.5.7.9 Implementationsdetails des Frameworks 179
2.3.6 Configuration&Change Management 184
2.3.6.1 Beispiel für Configuration Management Guidelines 184
2.3.6.1.1 Software-Element 184
2.3.6.1.2 Version 184
2.3.6.1.3 Checkin-Checkout Modell 184
2.3.6.1.4 Konfiguration (Lineup) und Release (Baseline) 185
2.3.6.1.5 Configuration Specification (Konfigurations-Identifikationsdokument KID) 185
2.3.6.1.6 Single Stream Versioning 185
2.3.6.1.7 Varianten von Versionen 185
2.3.6.1.8 Branches 186
2.3.6.1.9 Parallel Stream Versioning 186
2.3.6.1.10 File based System 187
2.3.6.1.11 View based System 187
2.3.6.1.12 Zustandsübergänge bei der Bearbeitung eines Software-Elementes 187
2.3.6.2 Beispiel Configuration Management mit CVS (Tutorial, Selbststudium) 187
2.3.6.2.1 Struktur des Projektes 187
2.3.6.2.2 Allgemeine Vorbereitungen 188
2.3.6.2.2.1 Download von CVS 188
2.3.6.2.2.2 Installation von CVS 188
2.3.6.2.2.3 Setzen des Classpath für den Taschenrechner 188
2.3.6.2.2.4 Anlegen des Projektverzeichnisses 188
2.3.6.2.2.5 Anlegen des Repository 189
2.3.6.2.2.6 Setzen der CVSROOT Variablen 189
2.3.6.2.2.7 Sonderbehandlung von Binärdateien einstellen 189
2.3.6.2.3 Erstellen des Main Trunk durch den Projektleiter 190
2.3.6.2.3.1 Anlegen des Projektes 190
2.3.6.2.3.2 Erzeugen des Package rechner 191
2.3.6.2.3.3 Erzeugen des Interfaces 191
2.3.6.2.3.4 Erzeugen einer Dummy Implementation des Interfaces 192
2.3.6.2.3.5 Aufnehmen der Dateien in das Projekt 192
2.3.6.2.3.6 Einchecken der Dateien 192
2.3.6.2.3.7 Labeln der Konfiguration 192
2.3.6.2.3.8 Erzeugen das Package gui und der Datei TaschenRechnerGUI 192
2.3.6.2.3.9 Erzeugen der Main Klasse TaschenRechner.java 193
2.3.6.2.3.10 Anlegen der Branches 193
2.3.6.2.3.11 Zugriff auf zentrales Repository ermöglichen 194
2.3.6.2.4 Entwickeln der GUI durch Entwickler1 194
2.3.6.2.4.1 Auschecken der Arbeitskopie vom Branch Entwickler-1-GUI 194
2.3.6.2.4.2 Codieren der GUI Klasse 194
2.3.6.2.4.3 Einchecken der Arbeitskopie in den Branch Entwickler-1-GUI 197
2.3.6.2.5 Entwickeln des Rechners durch Entwickler2 197
2.3.6.2.5.1 Auschecken der Arbeitskopie vom Branch Entwickler-2-Rechner 197
2.3.6.2.5.2 Codieren der Rechnerlogik – Datei TaschenRechnerImpl.java 197
2.3.6.2.5.3 Codieren der GUI Klasse als Testtreiber 198
2.3.6.2.5.4 Einchecken der Arbeitskopie in den Branch Entwickler-2-Rechner 199
2.3.6.2.6 Zusammenführen (Mergen) der Entwicklungsstränge durch den Projektleiter 199
2.3.6.2.7 Darstellung der Entwicklungsstränge 200
2.3.6.3 Beispiel Configuration Management Unterstützung durch Rational Rose 202
2.3.6.3.1 Controlled Units 202
2.3.6.3.2 Verwalten von controlled units 202
2.3.6.4 Bereitstellen eines Project Repository 203
2.3.6.5 Erstellen von Base Lines 203
2.3.6.6 Erstellen von Change Requests 203
2.3.7 Deployment 203
2.3.7.1 Erstellen von Training Material 203
2.3.7.2 Erstellen von User Support Material 204
2.3.7.3 Erstellen von Release Notes 204
2.3.7.4 Ausliefern an ß-Tester 204
2.3.8 Project Management 204
2.3.8.1 Erstellen einer Risk List 204
2.3.8.2 Erstellen eines Iteration Plan 204
2.3.8.3 Aquire Staff 204
2.3.8.4 Erstellen von Work Orders 204
2.3.9 Environment 205
2.3.9.1 Erstellen von Templates 205
2.3.9.2 Erstellen von Guidelines 205
2.3.9.3 Bereitstellen von Tools 205
2.4 Phasen des RUP 205
2.4.1 Zitate zum Begriff Komplexität 205
2.4.2 Die Struktur komplexer Systeme 205
2.4.3 Grundlegende Konzepte des RUP 206
2.4.3.1 Use Case Driven 206
2.4.3.2 Architecture Centric 207
2.4.3.3 Iterative and Incremental 207
2.4.3.4 Strukturierung des Entwicklungsprozesses 207
2.4.3.4.1 Phasen-Aktivitäten Diagramm 207
2.4.3.4.2 Disciplines 208
2.4.3.4.3 Zyklen 208
2.4.3.4.4 Phasen 208
2.4.3.4.5 Milestones 209
2.4.4 Phasen des RUP und zugehörige primäre Aktivitäten 209
2.4.4.1 Inception 209
2.4.4.2 Elaboration 210
2.4.4.3 Construction 211
2.4.4.4 Transition 212
3 Komponentenbasierte Softwareentwicklung am Beispiel von Enterprise Java Beans 212
3.1 Literatur 212
3.2 Introduction 212
3.2.1 Distributed Object Architectures 213
3.2.1.1 Three Tier Architecture 213
3.2.1.2 Stub und Skeleton 213
3.2.1.3 Interface und Server Technologie am vereinfachten Modell 214
3.2.2 Server-Side Components (Selbststudium) 215
3.2.3 Component Transaction Monitors (Selbststudium) 216
3.3 Architectural Overview 217
3.3.1 Beispiel CruiseBean 217
3.3.2 Entity beans 218
3.3.3 Beispiel TravelAgentBean 218
3.3.4 Session beans 220
3.3.5 The bean container contract 221
3.3.6 Beispiel Cruise Interface and Class CruiseHome 221
3.3.7 Classes and Interfaces 221
3.3.8 Implementation des remote interface 222
3.3.9 Implementation des home interface 223
3.3.10 Beispiel deployment descriptor 224
3.3.11 Deployment descriptors and JAR files 227
3.4 Example Cruise 228
3.5 Container and Bean Managed Persistence of Entity Beans 228
3.5.1 Container Managed Persistence 228
3.5.2 Bean Managed Persistence 228
3.6 Stateful and stateless session beans 228
3.6.1 Stateful session beans 228
3.6.2 Stateless session beans 229
3.7 Resource Management 229
3.7.1 Problem 229
3.7.2 Pooling 229
3.7.3 Swapping 230
3.7.4 The Life Cycle of an Entity Bean 230
3.7.4.1 States 230
3.7.4.2 Does Not Exist 231
3.7.4.3 Pooled 231
3.7.4.4 Ready 231
3.7.4.4.1 Transition from pooled to ready via creation 231
3.7.4.4.2 Transition from pooled to ready via activation 232
3.7.4.4.3 Transition from ready to pooled via passivation 232
3.7.4.4.4 Transition from ready to pooled via removal 232
3.7.4.4.5 Anmerkungen 232
3.7.5 The Life Cycle of a Stateless Session Bean 233
3.7.5.1 States 233
3.7.5.2 DoesNotExist 233
3.7.5.3 Method-Ready Pool 233
3.7.5.3.1 Transition to the Method-Ready Pool 233
3.7.5.3.2 Life in the Method-Ready Pool 234
3.7.5.3.3 Transition out of the Method-Ready Pool 234
3.7.6 The Life Cycle of a Stateful Session Bean 234
3.7.6.1 The Activation Mechanism 234
3.7.6.2 States 235
3.7.6.3 Does Not Exist 235
3.7.6.4 Method Ready 235
3.7.6.4.1 Transition to Method Ready 235
3.7.6.4.2 Transition to Does Not Exist 235
3.7.6.5 Passivated 235
3.7.6.5.1 Transition to Passivated 235
3.7.6.5.2 Transition to Method Ready 236
3.8 Primary Services 236
3.8.1 Overview 236
3.8.2 Concurrency 236
3.8.3 Persistence 237
3.8.4 Distributed Objects 238
3.8.5 Naming 239
Software-Engineering 1
Themenüberblick:
Architektur grafischer Benutzungsoberflächen (AWT, Swing, MFC)
The Unified Modeling Language (UML)
The Unified Software Development Process (RUP)
Projekt (MS Project, CVS)
Frameworks (EJB)
Architektur grafischer Benutzungsoberflächen
1 AWT
1 Beispiel Calculator
Das Beispiel „Taschenrechner“ liefert einen Überblick über eine Reihe von Techniken. Diese Techniken werden im Detail erst beim Studium der nachfolgenden Skriptabschnitte verständlich.
Fenster
AWT
SimpleCalculatorAufgabe1
public class Calculator extends Frame implements ActionListener {
public static void main( String[] args ) {
Frame frame = new Calculator( "Calculator" );
frame.addWindowListener(
new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
Runtime.getRuntime().exit( 0 );
}
}
);
frame.pack();
frame.setVisible( true );
}
private TextField numberField = null;
private Panel controlPanel = null;
private double leftOperand = 0.0, rightOperand = 0.0, result = 0.0;
private boolean operandBufferEmpty = true;
private char operand = '=';
public Calculator(String title) {
super(title);
setBackground( Color.red ); setForeground( Color.black );
setLayout( new BorderLayout() );
// control panel
controlPanel = new Panel();
controlPanel.setLayout( new GridLayout( 4, 4, 1, 1 ) );
// GridLayout(int rows, int cols, int hgap, int vgap)
// Creates a grid layout with the specified number of rows and columns.
String[] controlNames = { "7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+" };
Button button = null;
for ( int i = 0; i < controlNames.length; i++ ) {
controlPanel.add( button = new Button( controlNames[ i ] ) );
button.addActionListener( this );
}
// number field
numberField = new TextField( "0", 9 );
// insert control panel and number field in frame
add( "South", numberField ); add( "Center", controlPanel );
}
public void actionPerformed( ActionEvent e ) {
// get first character of the pressed buttons title
String arg = ( (Button) e.getSource()).getLabel();
char arg0 = arg.charAt( 0 );
// initialize operand buffer
double operandBuffer = 0.0;
switch( arg0 ) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if ( operandBufferEmpty ) numberField.setText( arg );
else numberField.setText( numberField.getText() + arg );
operandBufferEmpty = false;
return;
case '.':
if ( operandBufferEmpty ) numberField.setText( "0." );
else numberField.setText( numberField.getText() + arg );
operandBufferEmpty = false;
return;
case '*': case '/': case '+': case '-':
try { operandBuffer = Double.valueOf( numberField.getText() ).doubleValue(); }
catch ( NumberFormatException nfe ) { return; }
operandBufferEmpty = true;
leftOperand = operandBuffer;
operand = arg0;
return;
case '=':
try { operandBuffer = Double.valueOf( numberField.getText() ).doubleValue(); }
catch ( NumberFormatException nfe ) { return; }
operandBufferEmpty = true;
rightOperand = operandBuffer;
switch ( operand ) {
case '/': result = leftOperand / rightOperand; break;
case '*': result = leftOperand * rightOperand; break;
case '-': result = leftOperand - rightOperand; break;
case '+': result = leftOperand + rightOperand; break;
}
numberField.setText( String.valueOf( result ) );
leftOperand = 0.0;
rightOperand = 0.0;
operand = '=';
return;
}
}
}
2 AWT und Swing
1 AWT
Im Gegensatz zu den meisten anderen Programmiersprachen wurde Java von Anfang an mit dem Anspruch entwickelt, ein vielseitiges, aber einfach zu bedienendes System für die Gestaltung grafischer Oberflächen zur Verfügung zu stellen. Das Resultat dieser Bemühungen steht seit dem JDK 1.0 als Grafikbibliothek unter dem Namen Abstract Windowing Toolkit (AWT) zur Verfügung.
2 Swing
Neben dem AWT gibt es eine zweite Bibliothek für die Gestaltung grafischer Benutzeroberflächen. Sie heißt Swing und ist seit der Version 1.1 fester Bestandteil des Java Development Kit. Da Swing-Anwendungen in ihren Möglichkeiten weit über das hinausgehen, was das AWT bietet, werden heute die meisten GUI-Programme mit Swing geschrieben.
Es macht Sinn, sich zunächst mit dem AWT zu beschäftigen.
Einerseits ist das AWT einfacher zu erlernen, denn es ist weniger umfangreich als Swing. Zum anderen werden viele der AWT-Features auch in Swing benötigt, und das angeeignete Wissen ist nicht verloren.
Drittens gibt es auch heute noch Anwendungen für das AWT, etwa bei der Applet-Programmierung, oder wenn die Verwendung von plattformspezifischen Dialogelementen zwingend erforderlich ist.
3 Überblick über AWT Components
[pic]
3 Erstellen eines normalen Fensters oder eines Dialogs
1 Beispiel zum Erzeugen eines Frames
SimpleFrameManipulations
CreateFrame
/* CreateFrame.java */
import java.awt.*;
public class CreateFrame
{
public static void main(String[] args)
{
// Anlegen eines Fensters
Frame frame = new Frame ("Frame entfernen");
Frame.setSize(300,200);
// Zuordnen eines Layoutmanagers
frame.setLayout(new FlowLayout(FlowLayout.LEFT,20,20);
// Einfügen von Dialogelementen
frame.add(new Button("Button 1"));
frame.add(new Button("Button 2"));
frame.add(new Button("Button 3"));
frame.add(new Button("Button 4"));
frame.add(new Button("Button 5"));
// Anzeigen des Fensters
frame.pack();
frame.setVisible(true);
// Pause von 3 Sekunden
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
//nichts
}
// Verstecken des Fensters
frame.setVisibe(false);
// Freigeben der Ressourcen
frame.dispose();
// Beenden des Programmes
System.exit(0);
}
}
2 Aktionen zum Erstellen
1 Anlegen eines Dialogfensters
Da Java prinzipiell keinen Unterschied zwischen Fenstern zur Ausgabe eines Dialogs und solchen zur Anzeige von Grafiken macht, ist es möglich, ein Dialogfenster wahlweise aus Frame oder Dialog abzuleiten.
Die Klasse Dialog erlaubt es, das Verändern der Fenstergröße durch den Anwender zu unterbinden, und bietet die Möglichkeit, den Dialog modal zu machen. Dadurch wird die Interaktion des Anwenders mit anderen Fenstern der Anwendung bis zum Schließen des Dialogfensters blockiert. Im Gegensatz zu Frame fehlt jedoch die Möglichkeit, eine Menüleiste zu erzeugen oder dem Fenster ein Icon zuzuordnen.
2 Zuordnen eines Layoutmanagers
Die Layoutmanager sind in Java für die Anordnung der Dialogelemente im Fenster verantwortlich. Jeder Layoutmanager verfolgt dabei eine eigene Strategie, Elemente zu plazieren und in der Größe so anzupassen, dass sie aus seiner Sicht optimal präsentiert werden.
Die Zuordnung eines Layoutmanagers zu einem Fenster wird in der Klasse Container realisiert. Container ist direkt aus Component abgeleitet, und beide zusammen bilden das Gerüst für alle anderen Fensterklassen. Die Klasse Container stellt eine Methode setLayout zur Verfügung, mit der der gewünschte Layoutmanager dem Fenster zugeordnet werden kann:
public void setLayout(LayoutManager mgr)
Java stellt standardmäßig die fünf Layoutmanager FlowLayout, GridLayout, BorderLayout, CardLayout und GridBagLayout zur Verfügung. Der einfachste Layoutmanager ist FlowLayout, er positioniert die Dialogelemente zeilenweise hintereinander. Passt ein Element nicht mehr in die aktuelle Zeile, so wird es in der nächsten plaziert usw.
3 Einfügen von Dialogelementen
Das Einfügen von Dialogelementen in das Fenster erfolgt mit der Methode add der Klasse Container:
Sollen Komponenten, die bereits an das Fenster übergeben wurden, wieder daraus entfernt werden, so kann dazu die Methode remove verwendet werden. Als Parameter ist dabei das zu löschende Objekt zu übergeben.
Container stellt auch Methoden zur Verfügung, um auf die bereits eingefügten Dialogelemente zuzugreifen:
Mit getComponentCount kann die Anzahl aller eingefügten Komponenten ermittelt werden. getComponent liefert die Komponente mit dem angegebenen Index, und getComponents gibt ein Array mit allen eingefügten Komponenten zurück.
4 Anzeigen des Dialogfensters
Wurden alle Komponenten an den Container übergeben, kann der Dialog formatiert und durch einen Aufruf von setVisible angezeigt werden. Zweckmäßigerweise sollte vorher die Methode pack der Klasse Window aufgerufen werden, um die Größe des Fensters an den zur Darstellung der Dialogelemente erforderlichen Platz anzupassen.
3 Beispiel Anpassen eines Frames
SimpleFrameManipulations
CustomizeFrame
/* CustomizeFrame.java */
import java.awt.*;
import java.awt.event.*;
public class CustomizeFrame
extends Frame
{
public static void main(String[] args)
{
CustomizeFrame wnd = new CustomizeFrame();
wnd.setSize(300,200);
wnd.setLocation(50,50);
wnd.setVisible(true);
}
public CustomizeFrame()
{
super("");
assignTitle();
assignIcon();
assignCursor();
assignColors();
assignFont();
addWindowListener(new WindowClosingAdapter(true));
}
private void assignTitle()
{
setTitle("Veränderte Fensterelemente");
}
private void assignIcon()
{
Image img = getToolkit().getImage("testicon.gif");
MediaTracker mt = new MediaTracker(this);
mt.addImage(img, 0);
try {
//Warten, bis das Image vollständig geladen ist,
mt.waitForAll();
} catch (InterruptedException e) {
//nothing
}
setIconImage(img);
}
private void assignCursor()
{
setCursor(new Cursor(Cursor.WAIT_CURSOR));
}
private void assignColors()
{
setForeground(Color.green);
setBackground(Color.blue);
}
private void assignFont()
{
setFont(new Font("Serif", Font.PLAIN, 28));
}
public void paint(Graphics g)
{
g.drawString("Test in Vordergrundfarbe",10,70);
}
}
4 Eventmodell
1 Beispiel ActionEvent
public class calculator extends Frame implements ActionListener {
public static void main( String[] args ) {
Frame frame = new Calculator( "Calculator" );
frame.addWindowListener(
new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
Runtime.getRuntime().exit( 0 );
}
}
);
frame.pack();
frame.setVisible( true );
}
private TextField numberField = null;
public Calculator(String title) {
super(title);
setBackground( Color.blue );
setLayout(new FlowLayout(FlowLayout.LEFT));
Button button = null;
add(button = new Button( "Press" ) );
button.addActionListener(this);
numberField = new TextField( "No Comment", 15 );
add(numberField );
}
public void actionPerformed(ActionEvent e){
System.out.println("Pressed");
Numberfield.setText("Pressed");
}
}
2 Überblick
Delegation Event Model oder Delegation Based Event Handling
Ereignisgesteuerte Programmierung ist der Hauptbestandteil der Programmierung von Benutzungsoberflächen. Der Benutzer löst durch das Bewegen, Klicken der Maus, Eingabe von Text usw. Ereignisse aus, auf die das Programm reagiert. Applets können das genauso.
Die Statusänderungen der Eingabegeräte, also z.B. das Bewegen der Maus, das Drücken der Maustaste oder der Tasten auf der Tastatur, werden dem Java System in Form von Events gemeldet.
Das JDK-1.1 führte ein Ereignismodell ein, das auf der Weiterleitung eines Ereignisses von einer Quelle zu einem vorher zu installierenden Lauschobjekt (Listener) beruht. Ist kein Listener für dieses Ereignis aktiviert, wird es auch nicht weitergeleitet.
Events sind Objekte, die von einem anderen Objekt ausgesendet werden, der Event-Source.
Ein Event-Listener teilt der Event-Source sein Interesse an bestimmten Events mit, indem er eine ihrer Registrierungsmethoden aufruft.
Er bekommt fortan die entsprechenden Events mitgeteilt bis er sich wieder abmeldet.
Die Mitteilung besteht umgekehrt im Aufruf der passenden Methode im Event-Listener, die als Argument eine entsprechende Unterklasse von EventObject erhält.
Um sicher zu stellen, dass jeder Event-Listener die benötigten Methoden bereit stellt, müssen die passenden Schnittstellen implementiert werden.
Die Oberflächenkomponenten erben von der Klasse Component viele der dazu notwendigen Methoden.
Überschreibt sie der Programmierer, kann er die Reaktion des Oberflächenkomponenten auf Ereignisse selbst festlegen.
3 Ereignisse
1 Diagramm
Die Klassenhierarchie enthält eine Wurzelklasse für alle Ereignisse: java.util.EventObject. Damit unterscheiden sich Ereignisse nicht durch bestimmte Identifikatoren, sondern durch ihre Zugehörigkeit zu unterschiedlichen Klassen. Erst wenn verschiedene Ereignisse zu derselben Ereignisklasse gehören, bestimmt ein zusätzlicher Identifikator das Ereignis.
Die zu einem Ereignis gehörenden Daten (z.B. die aktuellen Koordinaten der Maus) sind nicht mehr direkt nach außen sichtbar. Stattdessen dienen spezielle Zugriffsmethoden der Abfrage der Werte.
Mittels getSource() erhält man das mit den Event verknüpfte Objekt, mittels getID() die den Event kennzeichnende Event-ID.
Das Ereignismodell unterscheidet zwischen primitiven Ereignissen (low-level event) und Ereignissen zur Interaktion verschiedener Komponenten (semantic event). Zu Komponenten zählen sowohl AWT-Komponenten als auch andere Einheiten (wie z.B. Timer), die in ein Ereignismodell passen.
2 Primitive Ereignisse
Als primitive Ereignisse implementiert das AWT im Paket java.awt.event:
ComponentEvent (Größen- und Positionsänderung), ContainerEvent (Hinzufügen oder Löschen einer Komponente aus dem Container),
FocusEvent (Abgabe und Zuweisung des Eingabefokus),
WindowEvent (Änderung eines Fensters),
InputEvent mit den Subklassen KeyEvent (Tastatur-Eingaben) und MouseEvent (Mausbewegungen und Maustasten).
3 Ereignisse zur Interaktion verschiedener Komponenten
ActionEvent(Aufforderung zur Ausführung einer Aktion),
AdjustmentEvent (Wertänderung, z.B. bei Scrollbar),
ItemEvent (Änderung der Einstellung einer Auswahl, z.B. bei Choice oder Checkbox) und
TextEvent (Änderung eines Textes).
4 Listener
1 Konzept
Lauschobjekte (Listener) empfangen und verarbeiten Ereignisse.
Um diese Funktion ausführen zu können, muss der Listener eine spezielle auf die jeweiligen Ereignisse angepasste Schnittstelle implementieren. Die Schnittstelle bestimmt, welche Methoden im Listener implementiert werden müssen, um das Ereignis verarbeiten zu können. Im Allgemeinen enthält die Schnittstelle Bearbeitungsmethoden für jedes Ereignis, das die zu bedienende Ereignisklasse repräsentiert.
Erst die Installation eines entsprechenden Listeners führt auch zur Weiterleitung des Ereignisses. Hierin liegt ein entscheidender Performanzgewinn. Insbesondere hochfrequente Ereignisse, die zum Beispiel beim Bewegen der Maus entstehen, werden nur geliefert, wenn die Komponente sie auch wirklich benötigt.
2 Listenerdiagramm
3 Listener-Schnittstellen
Für primitive Ereignisse enthält das AWT in java.awt.event die Listener-Schnittstellen ComponentListener,
ContainerListener,
MouseMotionListener,
MouseListener,
FocusListener,
KeyListener und
WindowListener,
für die Ereignisse zur Interaktion
ActionListener,
AdjustmentListener,
ItemListener und
TextListener.
Einige dieser Schnittstellen enthalten mehrere Methoden für verschiedene Ereignisse,
MouseListener z.B.
mouseClicked(),
mouseEntered(),
mouseExited(),
mousePressed() und
mouseReleased().
Diese Wahl wurde getroffen, um nicht für jedes Ereignis eine Listener-Klasse zu benötigen.
5 Ereignisquellen
1 Konzept
Eine Ereignisquelle liefert Ereignisse an Listener. Das können sowohl ein als auch mehrere Listener sein, je nachdem, wie sie installiert wurden. Ist kein Listener zu einem Ereignis installiert, so wird es nicht geliefert. Sendet die Quelle ein Ereignis an mehrere Listener, so darf der Programmierer über die Reihenfolge der bedienten Listener keine Annahme treffen. Er sollte also kein Programm schreiben, das von einer bestimmten Reihenfolge ausgeht. Jeder Listener erhält dabei eine Kopie des ursprünglichen Ereignisses, so dass eventuelle Änderungen des Ereignisses in einem Listener keine Wirkung auf einen anderen Listener haben.
2 Ereignisquellen
Quellen von primitiven Ereignissen sind die GUI-Komponenten des AWT:
Component ( ComponentEvent, FocusEvent, KeyEvent, MouseEvent, MouseMotionEvent),
Dialog ( WindowEvent) und
Frame ( WindowEvent)
Quellen von Ereignissen zur Interaktion von Komponenten sind
Button ( ActionEvent),
MenuItem ( ActionEvent),
List ( ActionEvent, ItemEvent),
Choice ( ItemEvent),
Checkbox ( ItemEvent),
CheckboxMenuItem ( ItemEvent) und
Scrollbar ( AdjustmentEvent).
Mit einer Klasse sind natürlich auch die von ihr abgeleiteten Klassen Quellen der entsprechenden Ereignisse.
6 Aktivierung von Lauschobjekten
Jede Quelle besitzt für die von ihr gelieferten Ereignisse Methoden, Listener für diese Ereignisse zu aktivieren. Will der Programmierer mehrere Listener je Ereignis aktivieren, verwendet er die addXXXListener()-Methoden, anderenfalls die setXXXListener()-Methoden. XXX steht dabei für das jeweilige Ereignis.
7 Beispiel MouseEventsDemoEventTechniken
1 Implementation Listener durch Frame
/* MouseEvents.java */
import java.awt.*;
import java.awt.event.*;
public class MouseEvents
extends Frame implements MouseListener
{
Canvas c1 = null;
Canvas c2 = null;
public static void main(String[] args)
{
MouseEvents wnd = new MouseEvents();
wnd.setVisible(true);
}
public MouseEvents()
{
super("Mausklicks");
setSize(500,400);
setLayout(new FlowLayout());
setLocation(200,100);
int fx = getSize().height / 2;
int fy = getSize().width / 2;
c1 = new Canvas();
c1.setBackground(Color.blue);
c1.setSize(fx, fy);
add(c1);
c2 = new Canvas();
c2.setBackground(Color.red);
c2.setSize(fx, fy);
add(c2);
pack();
addWindowListener(new WindowClosingAdapter(true));
c1.addMouseListener(this);
}
// Variante Frame implementiert MouseListener selbst
public void mouseClicked(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mousePressed(MouseEvent event)
{
Graphics g = c1.getGraphics();
int x = event.getX();
int y = event.getY();
g.drawOval(x-10,y-10,20,20);
}
}
2 Adapter
/* MouseEvents.java */
import java.awt.*;
import java.awt.event.*;
public class MouseEvents
extends Frame
{
Canvas c1 = null;
Canvas c2 = null;
public static void main(String[] args)
{
MouseEvents wnd = new MouseEvents();
wnd.setVisible(true);
}
public MouseEvents()
{
super("Mausklicks");
setSize(500,400);
setLayout(new FlowLayout());
setLocation(200,100);
int fx = getSize().height / 2;
int fy = getSize().width / 2;
c1 = new Canvas();
c1.setBackground(Color.blue);
c1.setSize(fx, fy);
add(c1);
c2 = new Canvas();
c2.setBackground(Color.red);
c2.setSize(fx, fy);
add(c2);
pack();
addWindowListener(new WindowClosingAdapter(true));
c1.addMouseListener(new MyMouseListener());
}
// Variante Frame erzeugt Objekt einer Klasse, die von MouseAdapter abgeleitet ist
class MyMouseListener extend MouseAdapter {
public void mousePressed(MouseEvent event)
{
Graphics g = c1.getGraphics();
int x = event.getX();
int y = event.getY();
g.drawOval(x-10,y-10,20,20);
}
}
}
3 Anonyme Klasse
/* MouseEvents.java */
import java.awt.*;
import java.awt.event.*;
public class MouseEvents
extends Frame
{
Canvas c1 = null;
Canvas c2 = null;
public static void main(String[] args)
{
MouseEvents wnd = new MouseEvents();
wnd.setVisible(true);
}
public MouseEvents()
{
super("Mausklicks");
setSize(500,400);
setLayout(new FlowLayout());
setLocation(200,100);
int fx = getSize().height / 2;
int fy = getSize().width / 2;
c1 = new Canvas();
c1.setBackground(Color.blue);
c1.setSize(fx, fy);
add(c1);
c2 = new Canvas();
c2.setBackground(Color.red);
c2.setSize(fx, fy);
add(c2);
pack();
addWindowListener(new WindowClosingAdapter(true));
// Variante Frame erzeugt Objekt einer Klasse, die von MouseAdapter abgeleitet ist
c1.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e) {
Graphics g = c1.getGraphics();
g.drawOval(e.getX(), e.getY(), 20 , 20);
}
}
);
}
}
8 Adapter Klassen
Wenn z.B. die MouseListener Klasse keine Schnittstelle sondern eine beerbbare normale Klasse wäre und Java Mehrfachvererbung erlauben würde, könnte man in der MouseListener Klasse für jedes Ereignis eine Standardreaktion implementieren. Eine spezifische von MouseListener abgeleitete Klasse müsste nur diejenige Handlerfunktion überschreiben, die neu definiert werden soll. Da es sich jedoch bei der Klasse MouseListener um eine reine Schnittstelle handelt ist keine der Handlerfunktionen implementiert. Daher müsste die von MouseListener abgeleitete Klasse wirklich alle Handlerfunktionen ausfüllen.
Es wäre jedoch für den Programmierer sehr umständlich, jede der enthaltenen Methoden ausfüllen zu müssen, auch wenn er nur ein einziges Ereignis (z.B. mousePressed()) behandeln will. Für jede der oben genannten Schnittstellen enthält das AWT deshalb eine sogenannte Adapter-Klasse, eine Klasse, die genau diese Schnittstelle implementiert. Diese Klassen kann der Programmierer erweitern und muss dann nur noch die für das gewünschte Ereignis zuständige Methode überschreiben.
Die vorhandenen Adapter sind :
ComponentAdapter,
ContainerAdapter,
FocusAdapter,
KeyAdapter,
MouseAdapter,
MouseMotionAdapter und
WindowAdapter.
Da die Schnittstellen für die Listener der Interaktionsereignisse jeweils nur eine Methode enthalten, existieren für sie keine Adapter.
9 Adapter als innere Klassen
Das Einerben eines Adapters funktioniert wegen der verbotenen Mehrfachvererbung nicht, wenn die Klasse bereits von einer anderen Klasse abgeleitet wurde.
In diesem Falle wird der Adapter als innere Klasse realisert.
10 Beispiele
1 Beispiel Schließen eines Frames mit Escape
Fenster
AWT
Events
SchliessenMitEscape
1 Implementation Keylistener
/* FrameManip.java */
import java.awt.*;
import java.awt.event.*;
public class FrameManip
extends Frame
implements KeyListener
{
public static void main(String[] args)
{
FrameManip wnd = new FrameManip();
}
public FrameManip()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
addKeyListener(this);
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drücken...",10,50);
}
public void keyPressed(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
public void keyReleased(KeyEvent event)
{
}
public void keyTyped(KeyEvent event)
{
}
}
2 Innerere Klasse und Adapter
import java.awt.*;
import java.awt.event.*;
public class FrameManip
extends Frame
{
public static void main(String[] args)
{
FrameManip wnd = new FrameManip();
}
public FrameManip()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
addKeyListener(new MyKeyListener());
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drücken...",10,50);
}
class MyKeyListener
extends KeyAdapter
{
public void keyPressed(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
}
}
3 Anonyme Klasse
import java.awt.*;
import java.awt.event.*;
public class Listing2804
extends Frame
{
public static void main(String[] args)
{
Listing2804 wnd = new Listing2804();
}
public Listing2804()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
addKeyListener(
new KeyAdapter() {
public void keyPressed(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
}
);
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drücken...",10,50);
}
}
4 Überlagern der Event-Handler in den Komponenten
Jede Ereignisquelle besitzt eine Reihe von Methoden, die für das Aufbereiten und Verteilen der Nachrichten zuständig sind. Soll eine Nachricht weitergereicht werden, so wird dazu zunächst innerhalb der Nachrichtenquelle die Methode processEvent aufgerufen. Diese verteilt die Nachricht anhand ihres Typs an spezialisierte Methoden, deren Name sich nach dem Typ der zugehörigen Ereignisklasse richtet. So ist beispielsweise die Methode processActionEvent für das Handling von Action-Events und processMouseEvent für das Handling von Mouse-Events zuständig:
protected void processEvent(AWTEvent e)
protected void processComponentEvent(ComponentEvent e)
protected void processFocusEvent(FocusEvent e)
Beide Methodenarten können in einer abgeleiteten Klasse überlagert werden, um die zugehörigen Ereignisempfänger zu implementieren. Wichtig ist dabei, dass in der abgeleiteten Klasse die gleichnamige Methode der Basisklasse aufgerufen wird, um das Standardverhalten sicherzustellen. Wichtig ist weiterhin, dass sowohl processEvent als auch processActionEvent usw. nur aufgerufen werden, wenn der entsprechende Ereignistyp für diese Ereignisquelle aktiviert wurde. Dies passiert in folgenden Fällen:
Wenn ein passender Ereignisempfänger über die zugehörige addEventListener-Methode registriert wurde.
Wenn der Ereignistyp explizit durch Aufruf der Methode enableEvents aktiviert wurde.
Die Methode enableEvents erwartet als Argument eine Maske, die durch eine bitweise Oder-Verknüpfung der passenden Maskenkonstanten aus der Klasse AWTEvent zusammengesetzt werden kann:
protected final void enableEvents(long eventsToEnable)
java.ponent
Die verfügbaren Masken sind analog zu den Ereignistypen benannt und heißen ACTION_EVENT_MASK, ADJUSTMENT_EVENT_MASK, COMPONENT_EVENT_MASK usw.
Das folgende Beispiel überlagert die Methode processKeyEvent in der Klasse Frame (die sie aus Component geerbt hat). Durch Aufruf von enableEvents wird die Weiterleitung der Tastaturereignisse aktiviert, und das Programm zeigt dasselbe Verhalten wie die vorigen Programme.
import java.awt.*;
import java.awt.event.*;
public class FrameManip
extends Frame
{
public static void main(String[] args)
{
FrameManip wnd = new FrameManip();
}
public FrameManip()
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
enableEvents(AWTEvent.KEY_EVENT_MASK);
}
public void paint(Graphics g)
{
g.setFont(new Font("Serif",Font.PLAIN,18));
g.drawString("Zum Beenden bitte ESC drücken...",10,50);
}
public void processKeyEvent(KeyEvent event)
{
if (event.getID() == KeyEvent.KEY_PRESSED) {
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
setVisible(false);
dispose();
System.exit(0);
}
}
super.processKeyEvent(event);
}
}
Diese Art der Ereignisbehandlung ist nur sinnvoll, wenn Fensterklassen oder Dialogelemente überlagert werden und ihr Aussehen oder Verhalten signifikant verändert wird. Alternativ könnte natürlich auch in diesem Fall ein EventListener implementiert und die entsprechenden Methoden im Konstruktor der abgeleiteten Klasse registriert werden.
Das hier vorgestellte Verfahren umgeht das Delegation Event Model vollständig und hat damit die gleichen inhärenten Nachteile wie das Event-Handling des alten JDK. Die Dokumentation zum JDK empfiehlt daher ausdrücklich, für alle »normalen« Anwendungsfälle das Delegation Event Model zu verwenden.
2 Key Events
Unter Windows werden alle Tastatureingaben an die fokussierte Komponente gesendet. Ein Empfänger für Key-Events muss das Interface KeyListener implementieren und bekommt Events des Typs KeyEvent übergeben. KeyEvent erweitert die Klasse InputEvent, die ihrerseits aus ComponentEvent abgeleitet ist, und stellt neben getID und getSource eine ganze Reihe von Methoden zur Verfügung, mit denen die Erkennung und Bearbeitung der Tastencodes vereinfacht wird.
Zeichentasten sind solche Tasten, mit denen Buchstaben, Ziffern oder sonstige gültige Unicode-Zeichen eingegeben werden, wie z.B. [a], [A], [B], [1], [2], [%], [+], aber auch [ESC], [LEER] oder [TAB].
Zu den Funktionstasten gehören beispielsweise [F1], [F2], [POS1] oder [CURSORLINKS], aber auch die Umschalttasten [STRG], [ALT] und [UMSCHALT].
Die Methode keyTyped wird immer dann aufgerufen, wenn eine Zeichentaste gedrückt wurde.
Beim Drücken einer Funktionstaste wird sie dagegen nicht aufgerufen. Im Gegensatz dazu wird keyPressed bei jedem Tastendruck aufgerufen, unabhängig davon, ob es sich um eine Zeichentaste oder eine Funktionstaste handelt.
Beide Methoden erhalten auch Tastatur-Repeats, werden also bei längerem Festhalten einer Taste wiederholt aufgerufen. Die Methode keyReleased wird aufgerufen, wenn eine gedrückte Taste losgelassen wurde, unabhängig davon, ob es sich um eine Zeichen- oder Funktionstaste handelt.
getKeyChar liefert das Zeichen, das der gedrückten Zeichentaste entspricht, also ein 'a', wenn die Taste [A] gedrückt wurde, und ein 'A', wenn die Tastenkombination [UMSCHALT]+[A] gedrückt wurde. getKeyCode liefert dagegen virtuelle Tastencodes, die in KeyEvent als symbolische Konstanten definiert wurden. Hier wird beim Drücken der Taste [A] immer der Code VK_A geliefert, unabhängig davon, ob [UMSCHALT] gedrückt wurde oder nicht. Die folgende Tabelle gibt eine Übersicht der wichtigsten virtuellen Keycodes der Klasse KeyEvent.
|Symbolischer Name |Bedeutung |
|VK_0 ... VK_9 |[0] ... [9] |
|VK_A ... VK_Z |[A] ... [Z] |
|VK_ENTER |[ENTER] |
|VK_SPACE |[LEER] |
|VK_TAB |[TAB] |
|VK_ESCAPE |[ESC] |
|VK_BACK_SPACE |[RÜCK] |
|VK_F1 ... VK_F12 |Die Funktionstasten [F1] ... [F12] |
|VK_HOME, VK_END |[HOME], [END] |
|VK_INSERT, VK_DELETE |[EINFG], [ENTF] |
|VK_PAGE_UP, VK_PAGE_DOWN |[BILDHOCH], [BILDRUNTER] |
|VK_DOWN, VK_UP |[CURSORHOCH], [CURSORRUNTER] |
|VK_LEFT, VK_RIGHT |[CURSORLINKS], [CURSORRECHTS] |
Am einfachsten ist es, innerhalb von keyTyped mit getKeyChar die Zeichentasten abzufragen. Dabei liefert getKeyChar stets den ASCII-Code der gedrückten Zeichentaste, Funktionstasten werden nicht übertragen. Der Rückgabewert von getKeyCode ist in diesem Fall immer KeyEvent.VK_UNDEFINED. Sollen dagegen auch Funktionstasten abgefragt werden, muß die Methode keyPressed überlagert werden. Hier ist etwas Vorsicht geboten, denn es wird auf alle Tastendrücke reagiert, und sowohl getKeyCode als auch getKeyChar liefern Werte zurück. Die Unterscheidung von Zeichen- und Funktionstasten kann in diesem Fall mit Hilfe von getKeyChar vorgenommen werden, deren Rückgabewert die Konstante KeyEvent.CHAR_UNDEFINED ist, wenn eine Funktionstaste gedrückt wurde.
Die is-Methoden sind bereits bekannt, mit ihnen können die Umschalttasten abgefragt werden. Das ist beispielsweise sinnvoll, um bei einer Funktionstaste herauszufinden, ob sie mit gedrückter Umschalttaste ausgelöst wurde oder nicht.
| |getKeyCode |getKeyChar |
|keyTyped |Zeichentaste: VK_UNDEFINED |Zeichentaste: Taste als char |
| |Funktionstaste: -- |Funktionstaste: -- |
|keyPressed |Zeichentaste: VK_... |Zeichentaste: Taste als char |
| |Funktionstaste: VK_... |Funktionstaste: CHAR_UNDEFINED |
Das Beispiel demonstriert die Abfrage der Tastaturereignisse. Es implementiert keyPressed, um die Funktionstasten [F1] bis [F3] und den Status der Umschalttasten abzufragen. Jeder Tastendruck wird in einen String übersetzt, in msg1 gespeichert und durch Aufruf von repaint auf dem Bildschirm angezeigt. Nach dem Loslassen der Taste wird die Anzeige wieder vom Bildschirm entfernt. Weiterhin wurde keyTyped überlagert, um die Zeichentasten abzufragen. Jeder Tastendruck wird in msg2 gespeichert und ebenfalls auf dem Bildschirm angezeigt. Im Gegensatz zu den Funktionstasten bleibt die Ausgabe auch erhalten, wenn die Taste losgelassen wird. Bei jedem weiteren Tastendruck wird sie um ein Zeichen ergänzt. Zusätzlich werden die einzelnen Ereignisse auf der Konsole dokumentiert.
/* KeyEvents.java */
import java.awt.*;
import java.awt.event.*;
public class KeyEvents
extends Frame
implements KeyListener
{
String msg1 = "";
String msg2 = "";
public static void main(String[] args)
{
KeyEvents wnd = new KeyEvents();
}
public KeyEvents()
{
super("Tastaturereignisse");
addKeyListener(this);
addWindowListener(new WindowClosingAdapter(true));
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
}
public void paint(Graphics g)
{
if (msg1.length() > 0) {
draw3DRect(g,20,50,250,30);
g.setColor(Color.black);
g.drawString(msg1,30,70);
}
if (msg2.length() > 0) {
draw3DRect(g,20,100,250,30);
g.setColor(Color.black);
g.drawString(msg2,30,120);
}
}
void draw3DRect(Graphics g,int x,int y,int width,int height)
{
g.setColor(Color.darkGray);
g.drawLine(x,y,x,y+height);
g.drawLine(x,y,x+width,y);
g.setColor(Color.white);
g.drawLine(x+width,y+height,x,y+height);
g.drawLine(x+width,y+height,x+width,y);
}
public void keyPressed(KeyEvent event)
{
msg1 = "";
System.out.println(
"key pressed: " +
"key char = " + event.getKeyChar() + " " +
"key code = " + event.getKeyCode()
);
if (event.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
int key = event.getKeyCode();
//Funktionstaste abfragen
if (key == KeyEvent.VK_F1) {
msg1 = "F1";
} else if (key == KeyEvent.VK_F2) {
msg1 = "F2";
} else if (key == KeyEvent.VK_F3) {
msg1 = "F3";
}
//Modifier abfragen
if (msg1.length() > 0) {
if (event.isAltDown()) {
msg1 = "ALT + " + msg1;
}
if (event.isControlDown()) {
msg1 = "STRG + " + msg1;
}
if (event.isShiftDown()) {
msg1 = "UMSCHALT + " + msg1;
}
}
}
repaint();
}
public void keyReleased(KeyEvent event)
{
System.out.println("key released");
msg1 = "";
repaint();
}
public void keyTyped(KeyEvent event)
{
char key = event.getKeyChar();
System.out.println("key typed: " + key);
if (key == KeyEvent.VK_BACK_SPACE) {
if (msg2.length() > 0) {
msg2 = msg2.substring(0,msg2.length() - 1);
}
} else if (key >= KeyEvent.VK_SPACE) {
if (msg2.length() < 40) {
msg2 += event.getKeyChar();
}
}
repaint();
}
}
3 Mouse Events
nur elektronisch
4 Component Events
nur elektronisch
5 Focus Events
nur elektronisch
5 Layoutmanager
1 Beispiel Layout
/* Layout.java */
import java.awt.*;
import java.awt.event.*;
public class Layout
extends Frame
{
public static void main(String[] args)
{
Layout wnd = new Layout();
wnd.setVisible(true);
}
public Layout()
{
super("Test Layout");
addWindowListener(new WindowClosingAdapter(true));
setLayout(new GridLayout(5,1));
// Panel fuer FlowLayout
Panel fp = new Panel();
//Layout setzen und Komponenten hinzufügen
fp.setLayout(new FlowLayout(FlowLayout.LEFT,20,20));
fp.add(new Button("Button 1"));
fp.add(new Button("Button 2"));
fp.add(new Button("Button 3"));
fp.add(new Button("Button 4"));
fp.add(new Button("Button 5"));
add(fp);
// Panel fuer GridLayout
Panel gp = new Panel();
//Layout setzen und Komponenten hinzufügen
gp.setLayout(new GridLayout(4,2));
gp.add(new Button("Button 1"));
gp.add(new Button("Button 2"));
gp.add(new Button("Button 3"));
gp.add(new Button("Button 4"));
gp.add(new Button("Button 5"));
gp.add(new Button("Button 6"));
gp.add(new Button("Button 7"));
add(gp);
// Panel fuer BorderLayout
Panel bp = new Panel();
//Layout setzen und Komponenten hinzufügen
bp.setLayout(new BorderLayout());
bp.add("North", new Button("Button 1"));
bp.add("South", new Button("Button 2"));
bp.add("West", new Button("Button 3"));
bp.add("East", new Button("Button 4"));
bp.add("Center", new Button("Button 5"));
add(bp);
// Geschachtelte Layoutmanager
//Panel 1
Panel panel1 = new Panel();
panel1.setLayout(new GridLayout(3,2));
panel1.add(new Button("Button1"));
panel1.add(new Button("Button2"));
panel1.add(new Button("Button3"));
panel1.add(new Button("Button4"));
panel1.add(new Button("Button5"));
//Panel 2
Panel panel2 = new Panel();
panel2.setLayout(new BorderLayout());
panel2.add("North", new Button("Button4"));
panel2.add("South", new Button("Button5"));
panel2.add("West", new Button("Button6"));
panel2.add("East", new Button("Button7"));
panel2.add("Center", panel1);
add(panel2);
pack();
}
}
2 Konzept
In vielen grafischen Oberflächen wird die Anordnung der Elemente eines Dialoges durch Angabe absoluter Koordinaten vorgenommen. Dabei wird für jede Komponente manuell oder mit Hilfe eines Ressourcen-Editors pixelgenau festgelegt, an welcher Stelle im Dialog sie zu erscheinen hat.
Da Java-Programme auf vielen unterschiedlichen Plattformen mit unterschiedlichen Ausgabegeräten laufen sollen, war eine solche Vorgehensweise für die Designer des AWT nicht akzeptabel. Sie wählten statt dessen den Umweg über einen Layoutmanager, der für die Anordnung der Dialogelemente verantwortlich ist. Um einen Layoutmanager verwenden zu können, wird dieser dem Fenster vor der Übergabe der Dialogelemente mit der Methode setLayout zugeordnet. Er ordnet dann die per add übergebenen Elemente auf dem Fenster an.
Jeder Layoutmanager implementiert seine eigene Logik bezüglich der optimalen Anordnung der Komponenten:
Neben den gestalterischen Fähigkeiten eines Layoutmanagers bestimmt in der Regel die Reihenfolge der Aufrufe der add-Methode des Fensters die tatsächliche Anordnung der Komponenten auf dem Bildschirm. Wenn nicht - wie es z.B. beim BorderLayout möglich ist - zusätzliche Positionierungsinformationen an das Fenster übergeben werden, ordnet der jeweilige Layoutmanager die Komponenten in der Reihenfolge ihres Eintreffens an.
Eines der Schlüsselkonzepte zur Realisierung komplexer, portabler Dialoge ist die Fähigkeit, Layoutmanager schachteln zu können. Dazu wird an der Stelle, die ein Sublayout erhalten soll, einfach ein Objekt der Klasse Panel eingefügt, das einen eigenen Layoutmanager erhält. Dieses Panel kann mit Dialogelementen bestückt werden, die entsprechend dem zugeordneten Unterlayout formatiert werden.
3 FlowLayout
Die Klasse FlowLayout stellt den einfachsten Layoutmanager dar. Alle Elemente werden so lange nacheinander in einer Zeile angeordnet, bis kein Platz mehr vorhanden ist und in der nächsten Zeile fortgefahren wird.
4 GridLayout
Ein GridLayout bietet eine größere Kontrolle über die Anordnung der Elemente als ein FlowLayout. Hier werden die Komponenten nicht einfach nacheinander auf dem Bildschirm positioniert, sondern innerhalb eines rechteckigen Gitters angeordnet, dessen Elemente eine feste Größe haben.
Das Programm übergibt dazu beim Aufruf des Konstruktors zwei Parameter, rows und columns, mit denen die vertikale und horizontale Anzahl an Elementen festgelegt wird:
Beim Aufruf von add werden die Komponenten dann nacheinander in die einzelnen Zellen der Gittermatrix gelegt. Analog zum FlowLayout wird dabei zunächst die erste Zeile von links nach rechts gefüllt, dann die zweite usw.
Die größere Kontrolle des Programms über das Layout besteht nun darin, dass der Umbruch in die nächste Zeile genau vorhergesagt werden kann. Anders als beim FlowLayout erfolgt dieser nicht erst dann, wenn keine weiteren Elemente in die Zeile passen, sondern wenn dort so viele Elemente plaziert wurden, wie das Programm vorgegeben hat.
Die Größe der Komponenten
Wenn wir die Größe des Fensters nicht durch einen Aufruf von pack, sondern manuell festgelegen, wird ein wichtiger Unterschied zwischen den beiden bisher vorgestellten Layoutmanagern deutlich.
Der Unterschied besteht darin, dass ein FlowLayout die gewünschte Größe eines Dialogelements verwendet, um seine Ausmaße zu bestimmen. Das GridLayout dagegen ignoriert die gewünschte Größe und dimensioniert die Dialogelemente fest in der Größe eines Gitterelements. Ein LayoutManager hat also offensichtlich die Freiheit zu entscheiden, ob und in welcher Weise er die Größen von Fenster und Dialogelementen den aktuellen Erfordernissen anpasst.
5 BorderLayout
Das BorderLayout verfolgt einen anderen Ansatz als die beiden vorigen Layoutmanager, denn die Positionierung der Komponenten wird nicht mehr primär durch die Reihenfolge der Aufrufe von add bestimmt. Statt dessen teilt das BorderLayout den Bildschirm in fünf Bereiche auf, und zwar in die vier Ränder und das Zentrum. Durch Angabe eines Himmelsrichtungs-Strings wird beim Aufruf von add angegeben, auf welchem dieser Bereiche die Komponente plaziert werden soll:
"South": unterer Rand
"North": oberer Rand
"East": rechter Rand
"West": linker Rand
"Center": Mitte
Bezüglich der Skalierung der Komponenten verfolgt BorderLayout einen Mittelweg zwischen FlowLayout und GridLayout. Während FlowLayout die Komponenten immer in ihrer gewünschten Größe belässt und GridLayout sie immer skaliert, ist dies bei BorderLayout von verschiedenen Faktoren abhängig:
Nord- und Südelement behalten ihre gewünschte Höhe, werden aber auf die volle Fensterbreite skaliert.
Ost- und Westelement behalten ihre gewünschte Breite, werden aber in der Höhe so skaliert, dass sie genau zwischen Nord- und Südelement passen.
Das Mittelelement wird in der Höhe und Breite so angepasst, dass es den verbleibenden freien Raum einnimmt.
Auch beim BorderLayout kann die Größe der Lücken zwischen den Elementen an den Konstruktor übergeben werden.
6 CardLayout
Das CardLayout ist in der Lage, mehrere Unterdialoge in einem Fenster unterzubringen und jeweils einen davon auf Anforderung des Programms anzuzeigen.
7 GridBagLayout
Das GridBagLayout ist ein komplexer Layoutmanager, der die Fähigkeiten von GridLayout erweitert und es ermöglicht, mit Hilfe von Bedingungsobjekten sehr komplexe Layouts zu erzeugen.
8 Null-Layout
Sollte auch die GridBagLayout Variante nicht genau genug sein, so bietet sich durch Verwendung eines Null-Layouts die Möglichkeit an, Komponenten durch Vorgabe fester Koordinaten zu plazieren.
6 Modale Dialoge
1 Beispiel Dialoge
/* Dialoge.java */
import java.awt.*;
import java.awt.event.*;
class YesNoDialog
extends Dialog
implements ActionListener
{
boolean result;
public YesNoDialog(Frame owner, String msg)
{
super(owner, "Ja-/Nein-Auswahl", true); //mit true wartet auf antwort
//Fenster
setBackground(Color.lightGray);
setLayout(new BorderLayout());
setResizable(false); //Hinweis im Text beachten
Point fatherLocation = owner.getLocation();
setLocation(fatherLocation.x + 30, fatherLocation.y + 30);
//Message
add("Center", new Label(msg));
//Buttons
Panel panel = new Panel();
panel.setLayout(new FlowLayout(FlowLayout.CENTER));
Button button = new Button("Ja");
button.addActionListener(this);
panel.add(button);
button = new Button("Nein");
button.addActionListener(this);
panel.add(button);
add("South", panel);
pack();
}
public void actionPerformed(ActionEvent event)
{
result = event.getActionCommand().equals("Ja");
setVisible(false);
dispose();
}
public boolean getResult()
{
return result;
}
}
public class Dialoge
extends Frame
implements ActionListener
{
public static void main(String[] args)
{
Dialoge wnd = new Dialoge();
wnd.setVisible(true);
}
public Dialoge()
{
super("Modale Dialoge");
setLayout(new FlowLayout());
setBackground(Color.lightGray);
Button button = new Button("Ende");
button.addActionListener(this);
add(button);
setLocation(100,100);
setSize(300,200);
setVisible(true);
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
if (cmd.equals("Ende")) {
YesNoDialog dlg;
dlg = new YesNoDialog(this, "Wollen Sie das Programm wirklich beenden?");
dlg.setVisible(true);
//Auf das Schließen des Dialogs warten...
if (dlg.getResult()) {
setVisible(false);
dispose();
System.exit(0);
}
}
}
}
2 Warten auf die Eingabe
Modale Dialoge sind solche, die alle Benutzereingaben des Programmes beanspruchen und andere Fenster erst dann wieder zum Zuge kommen lassen, wenn das Dialogfenster geschlossen wird. Eine wichtige Eigenschaft modaler Dialoge ist es, dass im Programm der Aufruf zur Anzeige des Dialogs so lange blockiert, bis der Dialog beendet ist. Auf diese Weise kann an einer bestimmten Stelle im Programm auf eine Eingabe gewartet werden und erst dann mit der Bearbeitung fortgefahren werden, wenn die Eingabe erfolgt ist.
Ein modaler Dialog muss immer aus der Klasse Dialog abgeleitet werden. Nur sie bietet die Möglichkeit, an den Konstruktor einen booleschen Wert zu übergeben, der festlegt, dass die übrigen Fenster der Anwendung während der Anzeige des Dialogs suspendiert werden.
Als erstes Argument des Konstruktors muss in jedem Fall ein Frame- oder Dialog-Objekt als Vaterfenster übergeben werden. Mit title kann der Inhalt der Titelzeile vorgegeben werden, und der Parameter modal entscheidet, ob der Dialog modal dargestellt wird oder nicht.
3 Beispiel VariableDialoge
nur elektronisch
2 Swing
1 Beispiel Geburtsmonat
Fenster
Swing
Geburtsmonat
/* Geburtsmonat.java */
import java.awt.event.*;
//import javax.swing.*; // Funktioniert nicht mit VisualCafe
import com.sun.java.swing.*;
public class Geburtsmonat
extends JFrame
implements ActionListener
{
private static final String[] MONTHS = {
"Januar", "Februar", "März", "April",
"Mai", "Juni", "Juli", "August",
"September", "Oktober", "November", "Dezember"
};
public Geburtsmonat()
{
super("Swing-Programm");
//Panel zur Namenseingabe hinzufügen
JPanel namePanel = new JPanel();
JLabel label = new JLabel(
"Name:",
new ImageIcon("testicon.gif"),
SwingConstants.LEFT
);
namePanel.add(label);
JTextField tf = new JTextField(30);
tf.setToolTipText("Geben Sie ihren Namen ein");
namePanel.add(tf);
namePanel.setBorder(BorderFactory.createEtchedBorder());
getContentPane().add("North", namePanel);
//Monatsliste hinzufügen
JList list = new JList(MONTHS);
list.setToolTipText("Wählen Sie ihren Geburtsmonat aus");
getContentPane().add("Center", new JScrollPane(list));
//Panel mit den Buttons hinzufügen
JPanel buttonPanel = new JPanel();
JButton button1 = new JButton("Metal");
button1.addActionListener(this);
button1.setToolTipText("Metal-Look-and-Feel aktivieren");
buttonPanel.add(button1);
JButton button2 = new JButton("Motif");
button2.addActionListener(this);
button2.setToolTipText("Motif-Look-and-Feel aktivieren");
buttonPanel.add(button2);
JButton button3 = new JButton("Windows");
button3.addActionListener(this);
button3.setToolTipText("Windows-Look-and-Feel aktivieren");
buttonPanel.add(button3);
buttonPanel.setBorder(BorderFactory.createEtchedBorder());
getContentPane().add("South", buttonPanel);
//Windows-Listener
addWindowListener(new WindowClosingAdapter(true));
}
public void actionPerformed(ActionEvent event)
{
String cmd = event.getActionCommand();
try {
//PLAF-Klasse auswählen
String plaf = "unknown";
if (cmd.equals("Metal")) {
plaf = "javax.swing.plaf.metal.MetalLookAndFeel";
} else if (cmd.equals("Motif")) {
plaf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
} else if (cmd.equals("Windows")) {
plaf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
}
//LAF umschalten
UIManager.setLookAndFeel(plaf);
SwingUtilities.updateComponetTreeUI(this);
} catch (UnsupportedLookAndFeelException e) {
System.err.println(e.toString());
} catch (ClassNotFoundException e) {
System.err.println(e.toString());
} catch (InstantiationException e) {
System.err.println(e.toString());
} catch (IllegalAccessException e) {
System.err.println(e.toString());
}
}
public static void main(String[] args)
{
Geburtsmonat frame = new Geburtsmonat();
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
}
}
Einige Besonderheiten von Swing
Unten befindet sich ein Panel mit drei Buttons, mit denen zwischen den drei vordefinierten Look-and-Feels umgeschaltet werden kann
Die beiden Panels wurden mit einer Umrandung versehen, und alle aktiven Elemente zeigen Tooltips an, wenn man mit dem Mauszeiger darauf zeigt.
Das Label besitzt neben seiner Beschriftung ein Icon.
2 Probleme des AWT
Da alle Fenster- und Dialogelemente von dem darunterliegenden Betriebssystem zur Verfügung gestellt wurden, ist es sehr schwierig, plattformübergreifend ein einheitliches Look-and-Feel zu realisieren.
Im AWT gibt es nur eine Grundmenge an Dialogelementen, mit denen sich aufwendige grafische Benutzeroberflächen nicht oder nur mit sehr viel Zusatzaufwand realisieren lassen.
3 Leichtgewichtige Komponenten
Im Gegensatz zum AWT benutzen Swing-Komponenten nur noch in sehr eingeschränkter Weise plattformspezifische GUI-Ressourcen.
Ein Swing-Button unter Windows wird nicht mehr vom Windows-UI-Manager dargestellt, sondern von Swing selbst gezeichnet
Der Grundstein für Komponenten, die von Java selbst erzeugt und dargestellt werden können, wurde im JDK 1.1 mit der Einführung der Lightweight Components (»leichtgewichtige« Komponenten) gelegt. Dabei wird die paint-Methode eines Component-Objects nicht mehr an die betriebssystemspezifische Klasse weitergeleitet, sondern in den Komponentenklassen überlagert und mit Hilfe grafischer Primitivoperationen selbst implementiert.
Die im AWT vorhandenen Dialogelemente werden im Gegensatz dazu als Heavyweight Components bezeichnet (»schwergewichtige« Komponenten).
Dass alle Komponenten selbst gezeichnet werden müssen, erfordert viel CPU-Leistung und eine Menge Hauptspeicher.
4 Pluggable Look-and-Feel
Eine der auf den ersten Blick spektakulärsten Eigenschaften von Swing ist die Möglichkeit, das Look-and-Feel (also das Aussehen und die Bedienung einer Anwendung) zur Laufzeit umschalten zu können, z.B. zwischen Swing, Motif und Windows (Pluggable Look-and-Feel ).
5 Das Model-View-Controller-Prinzip
Neben den äußerlichen Qualitäten wurde auch die Architektur des Gesamtsystems verbessert. Wichtigste "Errungenschaft" ist dabei das Model-View-Controller-Prinzip (kurz MVC genannt). Anstatt den gesamten Code in eine einzelne Klasse zu packen, werden beim MVC-Konzept drei unterschiedliche Bestandteile eines grafischen Elements sorgsam unterschieden:
Das Modell enthält die Daten des Dialogelements und speichert seinen Zustand.
Der View ist für die grafische Darstellung der Komponente verantwortlich.
Der Controller wirkt als Verbindungsglied zwischen beiden. Er empfängt Tastatur- und Mausereignisse und stößt die erforderlichen Maßnahmen zur Änderung von Model und View an.
Das Modell enthält praktisch die gesamte Verarbeitungslogik der Komponente. Ein wichtiger Aspekt ist dabei, dass ein Model mehrere Views gleichzeitig haben kann. Damit Veränderungen des Modells in allen Views sichtbar werden, wird ein Benachrichtigungsmechanismus implementiert, mit dem das Modell die zugeordneten Views über Änderungen informiert.
Bei den Swing-Dialogelementen wird eine vereinfachte Variante von MVC verwendet, die auch als Model-Delegate-Prinzip bezeichnet wird. Hierbei wird die Funktionalität von View und Controller in einem UI Delegate zusammengefasst.
6 Container und Menüs
1 Hauptfenster
1 Beispiel MDI
/* Mdi.java */
class DesktopFrame extends JFrame
{
private JDesktopPane desk;
public DesktopFrame()
{
super("DesktopFrame");
this.desk = new JDesktopPane();
desk.setDesktopManager(new DefaultDesktopManager());
setContentPane(desk);
addWindowListener(new WindowClosingAdapter());
}
public void addChild(JInternalFrame child, int x, int y)
{
child.setLocation(x, y);
child.setSize(200, 150);
child.setDefaultCloseOperation(
JInternalFrame.DISPOSE_ON_CLOSE
);
desk.add(child);
child.setVisible(true);
}
}
class ChildFrame
extends JInternalFrame
{
public ChildFrame(String title)
{
super("Child " + title, true, true);
setIconifiable(true);
setMaximizable(true);
setBackground(Color.green);
}
}
public class Mdi
{
public static void main(String[] args)
{
//Desktop erzeugen
DesktopFrame desktop = new DesktopFrame();
desktop.setLocation(100, 100);
desktop.setSize(400, 300);
desktop.setVisible(true);
//Zwei ChildFrames hinzufügen
desktop.addChild(new ChildFrame("1"), 10, 10);
desktop.addChild(new ChildFrame("2"), 20, 20);
}
}
2 JFrame
1 Struktur
Ein bedeutender Unterschied zwischen den AWT- und Swing-Hauptfenstern besteht in ihrer Komponentenstruktur und den sich daraus ergebenden Unterschieden in der Bedienung. Während die Komponenten eines AWT-Fensters direkt auf dem Fenster plaziert werden, besitzt ein Swing-Hauptfenster eine einzige Hauptkomponente, die alle anderen Komponenten aufnimmt.
Diese Hauptkomponente wird als RootPane bezeichnet und ist vom Typ JRootPane. Sie übernimmt die Rolle einer Verwaltungsinstanz für alle anderen Komponenten des Hauptfensters. Eine RootPane enthält folgende Komponenten:
• Eine aus Container abgeleitete GlassPane und
• eine aus JLayeredPane abgeleitete LayeredPane.
Die LayeredPane enthält ihrerseits zwei Unterkomponenten:
• Eine aus Container abgeleitete ContentPane und
• Eine aus JMenuBar abgeleitete Menüleiste.
Dabei ergibt sich folgende Struktur:
[pic]
Die GlassPane ist normalerweise durchsichtig und wird meist nicht zur Grafikausgabe benutzt. Sie könnte dann verwendet werden, wenn Effekte erzielt werden sollen, die das Fenster als Ganzes betreffen (und nicht seine einzelnen Dialogelemente). Eine (beispielsweise von JInternalFrame genutzte) Funktion besteht darin, Mausereignisse abzufangen, bevor sie an andere Komponenten weitergegeben werden.
Die LayeredPane enthält das Menü und die Dialogelemente der Anwendung.
2 Erzeugung
Die RootPane, und mit ihr die darin enthaltene GlassPane, LayeredPane und ContentPane, werden beim Anlegen des Fensters automatisch erzeugt (einzig die Menüleiste bleibt standardmäßig leer).
3 Zugriff
Alle Hauptfenster implementieren das Interface RootPaneContainer, das den Zugriff auf die RootPane vereinfacht. Einige seiner Methoden sind:
public JRootPane getRootPane()
public Container getContentPane()
public JLayeredPane getLayeredPane()
public Component getGlassPane()
javax.swing.RootPaneContainer
Um auf einem Hauptfenster Komponenten zu plazieren, ist es also nicht nötig, zunächst mit getRootPane die RootPane, dann mit getLayeredPane die LayeredPane und schließlich mit getContentPane die ContentPane zu beschaffen, sondern es kann direkt getContentPane aufgerufen werden.
Das Einfügen und Anordnen von Dialogelementen auf einem Hauptfenster erfolgt über dessen ContentPane. Die Aufrufe von add und setLayout werden damit nicht direkt auf dem Fenster ausgeführt, sondern auf dessen ContentPane, die über einen Aufruf von getContentPane beschafft werden kann.
3 JInternalFrame
1 Konzept
Bei vielen Programmen ist es üblich, dass sie ein einziges Hauptfenster besitzen und ihre zahlreichen, gleichzeitig geöffneten Kindfenster innerhalb dieses Hauptfensters anordnen. Diese unter Windows als MDI (Multiple Document Interface) bezeichnete Technik ist bei bestimmten Typen von Anwendungen mittlerweile weitverbreitet (z.B. bei Textverarbeitungen, Grafikprogrammen oder Entwicklungsumgebungen).
2 Der Desktop
Um die Kindfenster zu verwalten, wird die vordefinierte ContentPane durch eine Instanz der Klasse JDesktopPane ersetzt. Diese Klasse besitzt einen DesktopManager, der für die Verwaltung der Kindfenster zuständig ist. Der DesktopManager wird beispielsweise benachrichtigt (und führt alle dazu erforderlichen Aktionen aus), wenn ein Kindfenster verkleinert, vergrößert oder verschoben werden soll.
Der DesktopManager ist ein Interface, das eine Vielzahl von Methoden enthält. Mit der Klasse DefaultDesktopManager gibt es eine Standardimplementierung, die für viele Zwecke ausreichend ist.
3 Die Kindfenster
Die Kindfenster werden aus JInternalFrame abgeleitet.
JInternalFrame implementiert das Interface RootPaneContainer.
Mit setDefaultCloseOperation wird angegeben, wie das Fenster sich beim Schließen verhalten soll. Hier kann eine der Konstanten DO_NOTHING_ON_CLOSE, HIDE_ON_CLOSE oder DISPOSE_ON_CLOSE angegeben werden.
4 Beispiel SplashScreen
swing
SplashScreen
nur elektronisch
5 JWindow
Sie ist aus Window abgeleitet und dient wie diese dazu, ein rahmenloses Fenster zu erzeugen, das an beliebiger Stelle und in beliebiger Größe auf dem Bildschirm plaziert werden kann.
Ebenso wie JFrame besitzt auch JWindow eine RootPane mit der im vorigen Abschnitt beschriebenen Struktur.
6 JDialog
Die aus Dialog abgeleitete Klasse JDialog besitzt denselben strukturellen Aufbau wie JFrame und JWindow.
Als owner sollte der Aufrufer dem Konstruktor das Fenster übergeben, zu dem der Dialog logisch gehört.
7 Beispiel Quartal
Swing
Quartal
Zeigt die Verwendung von JOptionPane
nur elektronisch
8 JOptionPane
Eine weitere Möglichkeit, Swing-Dialoge zu erzeugen, steht mit der Klasse JOptionPane zur Verfügung. Diese ist in der Lage, einfache Dialoge, die lediglich ein Icon und einen Text oder ein Eingabefeld und eine Auswahl der Buttons "Yes", "No" und "Cancel" enthalten, mit einem einzigen Aufruf einer statischen Methode zu erzeugen.
9 JApplet
Die Klasse JApplet ist eine einfache Erweiterung von java.applet.Applet. Sie dient zur Entwicklung von Applets, die Swing-Dialogelemente zur Gestaltung der Oberfläche verwenden.
Wie bei den anderen Hauptfenstern ist auch hier der wichtigste Unterschied, dass JApplet die beschriebene RootPane-Struktur realisiert.
2 Menüs
1 Einfache Menüs
1 Beispiel SimpleMenue
Swing
SimpleMenue
/* SimpleMenue.java */
public class SimpleMenue
extends JFrame
implements ActionListener
{
public SimpleMenue()
{
super("Swing-Menütest");
addWindowListener(new WindowClosingAdapter());
JMenuBar menubar = new JmenuBar();
menubar.add(createFileMenu());
setJMenuBar(menuBar);
}
public void actionPerformed(ActionEvent event)
{
System.out.println(event.getActionCommand());
}
//---Private Methoden---------------
private JMenu createFileMenu()
{
JMenu menu = new JMenu("Datei");
menu.setMnemonic('D');
JMenuItem mi;
//Öffnen
mi = new JMenuItem("Oeffnen", 'f');
setCtrlAccelerator(mi, 'O');
mi.addActionListener(this);
menu.add(mi);
//Speichern
mi = new JMenuItem("Speichern", 'p');
setCtrlAccelerator(mi, 'S');
mi.addActionListener(this);
menu.add(mi);
//Separator
menu.addSeparator();
//Beenden
mi = new JMenuItem("Beenden", 'e');
mi.addActionListener(this);
menu.add(mi);
return menu;
}
private void setCtrlAccelerator(JMenuItem mi, char acc)
{
KeyStroke ks = KeyStroke.getKeyStroke(
acc, Event.CTRL_MASK
);
mi.setAccelerator(ks);
}
public static void main(String[] args)
{
SimpleMenue frame = new SimpleMenue();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
Alt D erzeugt DropDownMenue
f öffnet File
Ctrl O öffnet File auch ohne DropDownMenue
2 Grundlagen von Swing-Menüs
JMenuBar
In Swing können alle Hauptfenster mit Ausnahme von JWindow eine Menüleiste haben. Dabei handelt es sich um eine Instanz der Klasse JMenuBar, die dem Hauptfenster durch Aufruf von addJMenuBar hinzugefügt wird.
JMenu
Die einzelnen Menüs einer Menüleiste sind Instanzen der Klasse JMenu, die aus JMenuItem abgeleitet ist (Composite-Pattern).
Mit addSeparator wird eine Trennlinie hinter dem letzten Menüpunkt angefügt.
JMenuItem
Die Klasse JMenuItem repräsentiert Menüeinträge, also Elemente, die sich in einem Menü befinden. Dabei handelt es sich um Texte, die wahlweise mit einem Icon oder einem Häkchen versehen werden können.
Der an den Konstruktor übergebene String text legt den Menütext fest. Wahlweise kann zusätzlich ein Icon übergeben werden, das neben dem Menütext angezeigt wird.
Mit setMnemonic wird das mnemonische Kürzel des Menüeintrags festgelegt. Das ist ein (unterstrichen dargestellter) Buchstabe innerhalb des Menütexts, der bei geöffnetem Menü gedrückt werden kann, um den Menüeintrag per Tastatur aufzurufen.
Die als Acceleratoren oder Beschleunigertasten bezeichneten Tasten können auch dann verwendet werden, wenn das entsprechende Menü nicht geöffnet ist. Beschleuniger werden mit setAccelerator zugewiesen.
Mit den Methoden setEnabled und getEnabled kann auf den Aktivierungszustand des Menüeintrags zugegriffen werden. Wird false an setEnabled übergeben, wird der Eintrag deaktiviert, andernfalls aktiviert.
2 Weitere Möglichkeiten
1 Beispiel Extended Menue ( funktioniert nicht mit VisualCafe)
Achtung Fehler bei Verwendung von Visual Cafe :
Im JDK 1.3 arbeitet das Programm unter Windows fehlerhaft. Nach einmaligem Anklicken des Menüeintrags »Sicherheit« kann das Untermenü »Tools« nicht mehr per Maus aufgerufen werden, weil im Event-Thread eine NullPointerException ausgelöst wird. Die Tastaturbedienung ist dagegen weiterhin möglich. Dieser seit dem RC2 bekannte Fehler wurde leider bis zur endgültigen Version des JDK 1.3 nicht mehr behoben.
JDK1.1-1.3
/* ExtendedMenue.java */
public class ExtendedMenue
extends JFrame
{
public ExtendedMenue()
{
super("Extended Menue");
addWindowListener(new WindowClosingAdapter());
JMenuBar menubar = new JMenuBar();
menubar.add(createExtrasMenu());
setJMenuBar(menubar);
}
//---Private Methoden---------------
private JMenu createExtrasMenu()
{
JMenu ret = new JMenu("Extras");
ret.setMnemonic('X');
JMenuItem mi;
//Tools-Untermenü
ret.add(createToolsSubMenu());
//Separator
ret.addSeparator();
//Statuszeile und Buttonleiste
mi = new JCheckBoxMenuItem("Statuszeile");
mi.setMnemonic('z');
((JCheckBoxMenuItem)mi).setState(true);
ret.add(mi);
mi = new JCheckBoxMenuItem("Buttonleiste");
mi.setMnemonic('B');
ret.add(mi);
//Separator
ret.addSeparator();
//Offline, Verbinden, Anmelden
ButtonGroup bg = new ButtonGroup();
mi = new JRadioButtonMenuItem("Offline", true);
mi.setMnemonic('O');
ret.add(mi);
bg.add(mi);
mi = new JRadioButtonMenuItem("Verbinden");
mi.setMnemonic('V');
ret.add(mi);
bg.add(mi);
mi = new JRadioButtonMenuItem("Anmelden");
mi.setMnemonic('A');
ret.add(mi);
bg.add(mi);
//Separator
ret.addSeparator();
//Sicherheit
mi = new JMenuItem(
"Sicherheit",
new ImageIcon("lock.gif")
);
mi.setMnemonic('S');
mi.setHorizontalTextPosition(JMenuItem.LEFT);
ret.add(mi);
return ret;
}
private JMenu createToolsSubMenu()
{
JMenu ret = new JMenu("Tools");
ret.setMnemonic('T');
ret.add(new JMenuItem("Rechner", 'R'));
ret.add(new JMenuItem("Editor", 'E'));
ret.add(new JMenuItem("Browser", 'B'));
ret.add(new JMenuItem("Zipper", 'Z'));
ret.add(new JMenuItem("Snapper", 'S'));
ret.add(new JMenuItem("Viewer", 'V'));
return ret;
}
public static void main(String[] args)
{
ExtendedMenue frame = new ExtendedMenue();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
2 Untermenüs
Da Menu aus MenuItem abgeleitet ist, kann an die Methode add der Klasse Menu auch eine Instanz der Klasse Menu übergeben werden. Auf diese Weise lassen sich Menüs schachteln.
Der Name des Untermenüs erscheint dann an der Einfügestelle, und mit einem kleinen Pfeil wird angezeigt, dass es sich um ein Untermenü handelt.
3 Icons in Menüeinträgen
Einem Menüeintrag kann auch ein Icon zugeordnet werden. Icon ist ein Interface, das die abstrakten Eigenschaften eines Icons definiert. Es besitzt eine Implementierung ImageIcon, mit der sehr einfach aus einer gif- oder jpeg-Datei ein Icon erzeugt werden kann:
4 Checkboxes und Radiobuttons in Menüeinträgen
Die Kontrolle des Zustands der Buttons erfolgt mit einem ButtonGroup-Objekt. Es wird vor dem Erzeugen der Menüeinträge angelegt, und jeder JRadioButtonMenuItem wird mit add hinzugefügt:
Das ButtonGroup-Objekt sorgt automatisch dafür, dass zu jedem Zeitpunkt genau ein Eintrag selektiert ist.
3 Kontextmenue
1 Beispiel Kontextmenue
nur elektronisch
2 Kontextmenüs
In Swing-Programmen werden Kontextmenüs mit Hilfe der Klasse JPopupMenu erzeugt.
Um das Menü anzuzeigen, ist show aufzurufen. Die dazu erforderlichen Koordinaten gewinnt man am besten aus der aktuellen Position des Mauszeigers, die im Mausereignis mitgeliefert wird.
Die zum Aktivieren eines Kontextmenüs erforderliche Mausaktion kann von Plattform zu Plattform unterschiedlich sein. Die portabelste Lösung besteht darin, einen MouseListener auf der Komponente zu registrieren, und bei jedem Mausereignis mit isPopupTrigger abzufragen, ob es sich um eine Aktion zum Aufrufen eines Kontextmenüs handelte.
7 Swing Komponenten
1 Einfache Swing Komponenten
1 Beispiel Swing Components
Swing
SwingComponents
/* SwingComponents.java */
public class SwingComponents
extends JFrame
implements ActionListener, CaretListener, AdjustmentListener, ChangeListener
{
// Fuer Liste
static final String[] DATA = {
"Hund", "Katze", "Meerschweinchen", "Tiger", "Maus",
"Fisch", "Leopard", "Schimpanse", "Kuh", "Pferd",
"Reh", "Huhn", "Marder", "Adler", "Nilpferd"
};
private JList list;
// Fuer Combo Box
private static final String[] COLORS = {
"rot", "grün", "blau", "gelb"
};
// Fuer Radio Buttons
private ButtonGroup group = new ButtonGroup();
// Fuer Scrollbar
private JPanel coloredPanel;
private JScrollBar sbEast;
private JScrollBar sbSouth;
private JSlider slWest;
private JSlider slNorth;
private int blue = 100;
private int red = 200;
public SwingComponents()
{
super("Swing Components");
addWindowListener(new WindowClosingAdapter());
Container cp = getContentPane();
cp.setLayout(new GridLayout(4, 2));
// Text
JPanel tp = new JPanel();
tp.setBorder(BorderFactory.createLineBorder(Color.blue));
// Textfields
JTextField tf;
//Linksbündiges Textfeld mit "Hello, world"
tf = new JTextField("Hello, world");
tp.add(tf);
//Leeres Textfeld mit 20 Spalten
tf = new JTextField(20);
tp.add(tf);
//Textfeld mit "Hello, world" und 20 Spalten
tf = new JTextField("Hello, world", 20);
tf.addActionListener(this);
tf.addCaretListener(this);
tp.add(tf);
// Textarea
JTextArea ta = new JTextArea("Hello world \nHallo Welt \nHuhu \ng \nh \nEnde", 5, 30);
ta.setTabSize(4);
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
tp.add(new JScrollPane(ta));
cp.add(tp);
// Labels
JPanel lp = new JPanel();
lp.setBorder(BorderFactory.createLineBorder(Color.blue));
lp.setLayout(new GridLayout(5, 1));
JLabel label;
//Standardlabel
label = new JLabel("Standard-Label");
lp.add(label);
//Label mit Icon
label = new JLabel(
"Label mit Icon",
new ImageIcon("lock.gif"),
JLabel.CENTER
);
lp.add(label);
//Nur-Icon
label = new JLabel(new ImageIcon("lock.gif"));
lp.add(label);
//Icon auf der rechten Seite
label = new JLabel(
"Label mit Icon rechts",
new ImageIcon("lock.gif"),
JLabel.CENTER
);
label.setHorizontalTextPosition(JLabel.LEFT);
lp.add(label);
//Label rechts unten
label = new JLabel("Label rechts unten");
label.setHorizontalAlignment(JLabel.RIGHT);
label.setVerticalAlignment(JLabel.BOTTOM);
lp.add(label);
cp.add(lp);
// Buttons
JPanel bp = new JPanel();
bp.setBorder(BorderFactory.createLineBorder(Color.blue));
//OK-Button
JButton okButton = new DefaultButton("OK", getRootPane());
okButton.addActionListener(this);
bp.add(okButton);
//Abbrechen-Button
JButton cancelButton = new CancelButton("Abbrechen");
cancelButton.addActionListener(this);
bp.add(cancelButton);
//Hilfe-Button
JButton helpButton = new JButton("Hilfe");
helpButton.setMnemonic('H');
helpButton.addActionListener(this);
bp.add(helpButton);
//Test-Button
JButton testButton = new JButton("Test", new ImageIcon("testicon.gif"));
testButton.setMnemonic('T');
testButton.addActionListener(this);
testButton.setHorizontalTextPosition(JButton.LEFT);
testButton.setForeground(Color.blue);
testButton.setBackground(Color.green);
bp.add(testButton);
//Panel hinzufügen
cp.add(bp);
// CheckBox
JPanel cbp = new JPanel();
cbp.setBorder(BorderFactory.createLineBorder(Color.blue));
cbp.setLayout(new GridLayout(3, 1));
for (int i = 1; i = 2; --base) {
try {
i = Integer.parseInt("40",base);
System.out.println("40 base "+base+" = "+i);
} catch (NumberFormatException e) {
System.out.println(
"40 ist keine Zahl zur Basis "+base
);
}
}
}
Die Reaktion auf eine Ausnahme muss keinesfalls zwangsläufig darin bestehen, das Programm zu beenden. Statt dessen kann auch versucht werden, den Fehler zu beheben oder zu umgehen, um dann mit dem Programm fortzufahren.
Wird im obigen Programm die try-catch-Anweisung in die Schleife gesetzt, so fährt das Programm nach jedem Fehler fort und versucht, die Konvertierung zur nächsten Basis vorzunehmen:
Die Ausgabe des Programms lautet nun:
40 base 10 = 40
40 base 9 = 36
40 base 8 = 32
40 base 7 = 28
40 base 6 = 24
40 base 5 = 20
40 ist keine Zahl zur Basis 4
40 ist keine Zahl zur Basis 3
40 ist keine Zahl zur Basis 2
6 Beispiel MultipleCatchException
/* MultipleCatchException.java */
public class MultipleCatchException
{
public static void main(String[] args)
{
int i, j, base = 0;
String[] numbers = new String[3];
numbers[0] = "10";
numbers[1] = "20";
numbers[2] = "30";
try {
for (base = 10; base >= 2; --base) {
for (j = 0; j = locales.length - 1 ) localeIndex = 0;
try {
sleep( 1000 );
}
catch ( InterruptedException e ) {}
}
}
}
Der Konstruktor bestimmt die vorhandenen Datumsformate und speichert sie in der Instanzvariablen locales ab.
Die Methode run() geht das gespeicherte Formatfeld sekundenweise Schritt für Schritt durch, zeigt Datum und Uhrzeit in dem entsprechenden Format und den Namen dieses Formats an.
3 Die Klasse Locale
Locale ist vergleichbar mit dem Context für die Grafikausgabe.
A Locale object represents a specific geographical, political, or cultural region. An operation that requires a Locale to perform its task is called locale-sensitive and uses the Locale to tailor information for the user. For example, displaying a number is a locale-sensitive operation--the number should be formatted according to the customs/conventions of the user's native country, region, or culture.
The first argument to constructor is a valid ISO
Language Code. These codes are the lower-case two-letter codes as defined by ISO-639. You can find a full list of these codes at a number of sites, such as:
The second argument to constructor is a valid ISO Country
Code. These codes are the upper-case two-letter codes as defined by ISO-3166. You can find a full list of these codes at a number of sites, such as:
The second constructor requires a third argument--the Variant. The Variant codes are vendor and browser-specific. For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX.
If you want to see whether particular resources are available for the Locale you construct, you must query those resources. For example, ask the NumberFormat for the locales it supports using its getAvailableLocales method.
The JDK provides a number of classes that perform locale-sensitive operations. For example, the NumberFormat class formats numbers, currency, or percentages in a locale-sensitive manner. Classes such as NumberFormat have a number of convenience methods for creating a default object of that type.
A Locale is the mechanism for identifying the kind of object (NumberFormat) that you would like to get. The locale is just a mechanism for identifying objects, not a container for the objects themselves.
4 Die Klasse DataFormat
DateFormat is an abstract class for date/time formatting subclasses which formats and parses dates or time in a language-independent manner. The date/time formatting subclass, such as SimpleDateFormat, allows for formatting (i.e., date -> text), parsing (text -> date), and normalization. The date is represented as a Date object or as the milliseconds since January 1, 1970, 00:00:00 GMT.
getAvailableLocales()
Gets the set of locales for which DateFormats are installed.
getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
Gets the date/time formatter with the given formatting styles for the given locale.
Statische Funktion, liefert DateFormat Objekt als ein Formatierobjekt
format(Date date)
Formats a Date into a date/time string.
5 Beispiel SimpleGlobalMenue
public class SimpleMenue
extends JFrame
{
public SimpleMenue()
{
super("Swing-Globalisierungstest");
addWindowListener(new WindowClosingAdapter());
Locale locale = Locale.US;
// Locale locale = Locale.GERMANY;
ResourceBundle labelBundle = ResourceBundle.getBundle(
"ViewerBundle", locale );
String [] labels = new String[7];
labels[ 0 ] = labelBundle.getString( "searchButton" );
labels[ 1 ] = labelBundle.getString( "clearButton" );
labels[ 2 ] = labelBundle.getString( "executeButton" );
JPanel bp = new JPanel();
getContentPane().setLayout(new BorderLayout());
getContentPane().add("North", bp);
bp.add(new JButton(labels[0]));
bp.add(new JButton(labels[1]));
bp.add(new JButton(labels[2]));
}
public static void main(String[] args)
{
SimpleMenue frame = new SimpleMenue();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
public class ViewerBundle extends ListResourceBundle {
static final Object[][] labels = {
{ "searchButton", "search" },
{ "clearButton", "clear" },
{ "executeButton", "execute" }};
public Object[][] getContents() {
return labels;
}
}
public class ViewerBundle_de extends ViewerBundle {
static final Object[][] contents = {
{ "searchButton", "Suchen" },
{ "clearButton", "Loeschen" },
{ "executeButton", "Starten" }};
public Object[][] getContents() {
return contents;
}
}
public class ViewerBundle_en extends ViewerBundle {
static final Object[][] labels = {
{ "searchButton", "search" },
{ "clearButton", "clear" },
{ "executeButton", "execute" }};
public Object[][] getContents() {
return labels;
}
}
6 Die Klasse ResourceBundle
ResourceBundle objects represent a bundle of resources, which belong to a Locale object.
There is no object, which represents the set of bundles.
The set of bundles is modeled by static functions of the ResourceBundle class.
When the program needs a locale-specific resource, a String for example, the program can load it from the resource bundle that is appropriate for the current user's locale using the static getBundle method.
Resource bundle objects contain key/value pairs. The keys uniquely identify a locale-specific object in the bundle.
Each related subclass of ResourceBundle has the same base name plus an additional component that identifies its locale.
Each related subclass of ResourceBundle contains the same items, but the items have been translated for the locale represented by that ResourceBundle subclass.
Die Unified Modeling Language (UML) und der Rational Unified Software Development Process (RUP)
1 UML im Überblick
1 Dokumentation
1 Wozu dient die Dokumentation
Die Dokumentation dient:
• Zur Reflexion der eigenen (manchmal wirren) Gedanken,
• Zur Abspeicherung der eigenen Überlegungen im Hinblick auf einen Personalwechel oder eine spätere Wartung,
• Zur Unterstützung der Kommunikation mit Kollegen während der Analyse und Entwurfsphasen
• Zur Unterstützung vertraglicher Vereinbarungen
2 Anmerkungen zur Dokumentation
In allen Ingenieurdisziplinen werden üblicherweise Zeichnungen angefertigt. Man denke z.B. an den Haus- oder Maschinenbau. Diese Zeichnungen werden an Subunternehmer ausgeliefert, dienen als Grundlage für die Kosten- und Zeitabschätzung, für die Ressourcen und Arbeitsverteilung.
Die Zeichnungen sind das Modell des entstehenden Produkts.
Eine wohldefinierte und ausdrucksstarke Notation ermöglicht erst die Kommunikation mit anderen Entwicklern oder Kunden.
Die Designer müssen sicherstellen, dass das Modell die unterschiedlichen Anforderungen an das Produkt berücksichtigt.
Das Durchdenken alternativer Modelle führt zu einem tieferen Verständnis des Systems und damit letztendlich zu einem besseren Produkt.
Ein Problem der heutigen Softwareentwicklung besteht darin, dass zu schnell programmiert wird. Damit befindet man sich bereits auf einem niedrigen Abstraktionslevel der Systementwicklung.
Die Gründe hierfür sind vielfältig. Manager werden ängstlich, wenn kein Code produziert wird. Programmierer fühlen sich beim Programmieren sicherer als beim Bilden abstrakter Modelle.
Insgesamt wird aber inzwischen die Bildung von Modellen in der Software Engineering Community genauso akkzeptiert wie in anderen Ingenieurdisziplinen auch.
Die Dokumentation ist also wichtig ! Sie sollte aber nicht den Entwicklungsprozess steuern !
Hier hilft eine angemessene Formalisierung der Vorgehensweise. Bei einem 1-Mann-Wegwerf-Programm für 1 Tag genügt ggf. eine Kritzelei auf einem Briefumschlag. Bei 100-Mann-Projekten sind dagegen Bücher mit Entwurfsbeschreibungen erforderlich.
Wenn die Designer u. Programmierer eines Systems hochqualifiziert sind und bereits seit einiger Zeit eng zusammenarbeiten, kann die Dokumentation etwas sparsamer ausfallen als bei einer neu zusammengestellten Mannschaft von Hochschulabgängern.
Ein zu hoher Dokumentationsaufwand stabilisiert möglicherweise den Zustand eines Systems und verhindert damit die für eine Weiterentwicklung und Verbesserung erforderlichen Iterationen. Daher sollten zur Minimierung des Aufwandes möglichst CASE-Tools verwendet werden.
Die Dokumentation sollte inkrementell mit dem System wachsen. Sie bildet dann die Basis für die Reviews zur Qualitätssicherung.
2 Geschichte der UML
Bezeichnungen von einigen der bisher verwendeten Methoden :
Booch Methode
OMT von James Rumbaugh
OOSE/Objectory von Ivar Jacobson
Coad Yourdon oder OOA/OOD
Jede Methode hatte ihre eigenen Notationen, ein eigenes Vorgehensmodell oder Prozess und ihre eigenen Tools.
Im Laufe der Zeit ähnelten sich die Methoden immer mehr. Dies wurde von den Methoden-Gurus erkannt und führte zur Entwicklung von UML.
Ein zentrales Ziel bei der Entwicklung von UML war die Beendigung des Methodenkriegs.
Die Unified Modelling Language (UML) ist eine visuelle Modellierungssprache und wurde von Grady Booch, James Rumbaugh und Ivar Jacobson entworfen.
UML ist offen, d.h. Firmen dürfen die Sprache frei benutzen, Toolhersteller dürfen passende CASE-Tools erstellen.
1996 entstand ein UML Partner Konsortium aus Rose, DigitalEquipment, HP, IBM, Microsoft, Oracle, Texas Instruments und weitere Organisationen.
Im Januar 1997 wurde die Version 1.0 ausgeliefert.
Die führende Organisation zur Standardisierung im objektorientierten Bereich, OMG (Object Managment Group), hat die UML im Nov 97 als Standard akzeptiert und damit die Verantwortung für die weitere Entwicklung der UML übernommen.
3 Views
Eine in irgend einem Sinne vollstandige Darstellung eines Systems ist bei komplexen Systemen nicht möglich. Z.B. lässt sich ein Automotor niemals in einer einzigen Zeichnung darstellen.
Die Darstellungen zeigen in der Regel nur ausgewählte Ansichten (Views) des Systems.
Z.B. zeigen Darstellungen eines Softwaresystems das Vererbungsnetz bestimmter Schlüsselklassen oder alle Klassen, die von einer bestimmten Klasse verwendet werden oder die Verteilung auf die Hardware oder die Verzeichnisstruktur.
Die Einteilung und die Bezeichnung der Views hängt von der Sicht des Autors ab.
Vgl. RefMan S.24
2 RUP im Überblick
Jede Softwareerstellung sollte in einem festgelegten organisatorischen Rahmen erfolgen.
Ein Software Development Process definiert wer, was, wann und wie tun soll, um ein bestimmtes Ziel zu erreichen. Der Prozess dient damit als Hilfe für die beteiligten Entwickler.
Der Rational Unified Process beschreibt die durchzuführenden Aktivitäten (Activities) geordnet nach sogenannten Disciplines und die Reihenfolge des Arbeitsablaufes geordnet nach sogenannten Phasen.
Der Rational Unified Process basiert auf dem 1987 veröffentlichten Objectory Process (Ericcson) und der 1997 standardisierten UML.
3 Aktivitäten des RUP und die zugehörigen UML Diagramme
Man strukturiert die für ein Softwareprodukt aufzuwendenden Aktivitäten gemäß folgender Disciplines
Business Modeling
Requirements
Analyse
Design
Implementation
Test
Deployment
Configuration&Change Management
Project Management
Environment
Business Modeling wird in einer eigenen Vorlesung behandelt.
1 Business Modeling
Der Zweck des Business Modeling besteht darin,
die Struktur und die Dynamik einer Firma zu verstehen,
die aktuellen Probleme kennen zu lernen und Möglichkeiten für Verbesserungen zu erkunden,
Anforderungen für Softwaresysteme zur Automatisierung abzuleiten.
Das Business Modeling ist Gegenstand einer eigenen Vorlesung.
Wir betrachten im Folgenden ausschließlich Beispiele, bei denen der „Geschäftsprozess“ einfach und aus dem täglichen Leben bekannt ist.
Als Beispiele betrachten wir Getränkeautomat und Bibliotheksystem.
2 Requirements
1 Erstellen einer Vision
1 Beispiel Getränkeautomat
Die AutomatenApplication steuert die Ausgabe eines vom Kunden selektierten Gerätes.
Die Bezahlung kann in bar (einschließlich Scheinannahme) und per Karte erfolgen.
Die Bedienung erfolgt über Touchscreen.
Der Automat informiert einen zentralen Monitor über besondere Ereignisse.
Der Status des Automaten kann vom zentralen Monitor erfragt werden.
Im Rahmen der Wartung soll eine statistische Auswertung möglic sein.
Das System soll einfach wartbar sein.
2 Beispiel Bibliothek
Bibliothek:
Vision
Die Bibliothekssoftware soll die Buchausleihe an der FH unterstützen Bücher und Leser sollen registriert werden können.
Bücher sollen schnell gesucht werden können.
Bücher sollen reserviert werden können.
Automatische Mahnungen sollen versendet werden können.
Zur Einsparung von Personal soll ein automatisches Transportsystem mit Hochlager und Scanner zur Verfügung stehen.
Eine Fernausleihe soll möglich sein.
Use Case Buch Ausleihen
Gutfall:
Das System zeigt das Login Formular
Der Kunde editiert Name und Passwort
Das System zeigt die BuchNavigationsAnsicht
Der Kunde selektiert ein Buch und drückt „Ausleihen“
Das System beauftragt das HochregalSystem mit der Beschaffung und den Ausgabeautomaten mit der Ausgabe
Der Kunde logged sich aus.
Schlechtfall:
3 Konzept
Die Vision definiert die Kundensicht des Products. Sie enthält eine knappe Darstellung der wesentlichsten Bedürfnisse, ausgedrückt in der Terminologie des Kunden.
Die Vision bildet die Basis für die detaillierte Auflistung der Requirements.
2 Aufnehmen der Stakeholder Requests
Stakeholder = customer, end user, marketing person, and so on
Liste mit allen vom Kunden formulierten Anforderungen für das System.
Technisch werden diese Requests als Change Requests verwaltet.
3 Erstellen eines Use Case Models
1 Beispiel Getränkeautomat
Gutfall
Touchscreen zeigt die vorhandenen Flaschentypen
Der Kunde drückt den Button mit dem Cola Icon
(Der Automat prüft, ob Cola vorhanden ist)
Der Automat zeigt den Preis.
Der Kunde wirft zu viel Geld ein.
Der Automat wirft eine Cola Flasche aus.
Der Automat gibt den überzahlten Betrag zurück.
Schlechtfall
…wie oben
Der Kunde wirft einen Hosenknopf ein
Der Automat gibt den Knopf zurück
Der Automat zeigt die Meldung „Unzulässiges Zahlungsmittel“
Schlechtfall:
…wie oben
Der Automat zeigt die Meldung „Cola leer“
Der Automat gibt den eingegebenen Betrag zurück
Grafik
[pic]
2 Beispiel Bibliothek
[pic]
3 Überblick
Ein Use Case Diagram zeigt eine Reihe externer Actors und deren Beziehungen zu den Use Cases.
Der Actor kann ein Mensch oder ein anderes Softwaresystem oder ein Gerät sein.
Use Cases
unterstützen die Entscheidungen über funktionale Anforderungen
beschreiben die funktionalen Anforderungen
unterstützen die Überprüfung der Vereinbarkeit unterschiedlicher Anforderungen
erleichtern die Kommunikation zwischen den Entwicklern
bilden die Basis für den Entwurf des Systems
bilden die Basis für spätere Tests
erleichtern die Verfolgung der Anforderungen zu den zugehörigen Klassen und Funktionen
4 System
Das System wird durch ein Rechteck beschrieben.
Das System muss nicht notwendigerweise ein Softwaresystem sein. Es kann auch eine Maschine oder eine Firma sein.
5 Actor
1 Charakterisierung
Ein Actor interagiert mit dem System indem er Nachrichten sendet und empfängt.
Der Actor ist ein Typ und keine Instanz. Er repräsentiert damit eine Rolle und nicht einen individuellen Nutzer des Systems.
Der Actor hat einen Namen, der seine Rolle reflektiert.
Ein Use Case wird immer durch einen Actor initiiert, der eine Nachricht an das System sendet (Stimulus).
Die Aktoren bilden den Context des Systems. Der Context eines Subsystems oder einer Klasse besteht in der Regel ausschließlich aus Software.
Ein Actor Model Element hat als Standard Icon ein Strichmännchen.
2 Beziehungen zwischen Actors
In Use Cases werden z.B. Vererbungsbeziehungen benutzt, um das gemeinsame Verhalten von Actors zu beschreiben.
6 Use Case
1 Charakterisierung
Ein Use Case ist definiert als eine Menge von Sequenzen von Actions, welche ein beobachtbares Resultat für einen bestimmten Actor ergeben.
Jeder Use Case beschreibt eine spezifische Funktionalität des Systems.
Ein Use Case wird immer von einem Actor initiiert.
Ein Use Case liefert immer einen Wert an den Anwender. Dieser Wert repräsentiert den Wunsch des Anwenders.
Im Verlauf eines Use Case können Interaktionen mit dem Actor stattfinden (z.B. Maskeneingaben). Ein Use Case ist erst dann vollständig , wenn der Endwert produziert wurde.
Die Verbindung Actor Use Case wird auch als Communication Association bezeichnet.
Ein Use Case ist eine Klasse und keine Instanz.
Er beschreibt die Funktionalität als Ganzes einschließlich Alternativen, Fehler und Exceptions.
Eine Instanz des Use Case ist ein Scenario. Ein Scenario stellt einen ganz bestimmten Ausführungspfad dar, z.B. einen Fehlerpfad.
Use Cases können für das gesamte System wie für einzelne Klassen erstellt werden.
2 Spezifikation
Die Beschreibung des Use Case erfolgt meist in Textform.
Sie sollte folgende Aussagen enthalten :
Was soll mit dem Use Case erreicht werden ?
Welcher Actor intiiert den Use Case in welcher Situation ?
Welche Botschaften tauschen Actor und System aus ?
Welche alternativen Ausführungen können in Abhängigkeit von welchen Bedingungen auftreten ?
Wann ist der Use Case beendet und welchen Wert liefert er an den Actor ?
Eine allgemeine textuelle Beschreibung kann durch spezielle Szenarien ergänzt werden. Diese beschreiben eine spezifische Situation unter Verwendung konkreter Instanzen von Actor und Use Case.
Meist beschreibt man ein Szenario für den Normalfall (Main Flow of Events) und mehrere Szenarien für besondere Ausnahmesituationen (Exceptional Flow of Events).
Jede Sequenz stellt einen der möglichen Kontrolflüsse dar.
Szenarios bilden die Instanzen eines Use Case.
Die graphische Darstellung der Szenarien erfolgt typischerweise mit einem Sequenzdiagram (vgl. weiter unten).
Ein Use Case kann auch durch ein Activity Diagram oder ein Statechart Diagram (vgl. weiter unten) beschrieben werden.
Use Cases werden in der Sprache und Teminologie des Kunden beschrieben.
Die Funktionalität wird so beschrieben wie sie der Nutzer wahrnimmt. Insbesondere wird nicht beschrieben, wie das System die Funktionalität erfüllt.
Eine nützliche Methode zum Beschreiben und Durchspielen der Use Cases besteht im Rollenspiel. Eine Person spielt die Rolle des Actors, eine andere die Rolle des Systems.
3 Beziehungen zwischen Use Cases
Generalization Relationship
Das Kind kann überall dort verwendet werden, wo der Vater erscheint.
Z.B. kann der Use Case „Identifizierung eines Kunden“ durch den Use Case „Scannen der Netzhaut“ oder den Use Case „Pruefen des Passwortes“ spezialisiert werden.
Include Relationship
Wenn mehrere Use Cases dieselben Szenarien beinhalten, können diese in einem gemeinsamen Use Case modelliert werden.
Z.B. beinhalten die Use Cases „GeldWechseln“ und „KontostandAnzeigen“ das Verhalten des Use Case „ValidiereKunden“.
Grouping
Use Cases können mittels Packages (vgl. weiter unten) gruppiert werden.
???Extends Relationship
Ein Use Case kann einen anderen erweitern. Dies bedeutet, dass er einige Szenarien beinhaltet, die ggf. optional an bestimmten Stellen des erweiterten Use Cases in ähnlicher Weise wie Unterprogramme durchlaufen werden.
7 Erweiterung der UML
Die Klassifizierung der Model Elemente erfolgt durch sogenannte Stereotypen.
Das Strichmännchen ist das Standard Icon für den Stereotyp .
Die Stereotypen können in der Regel durch ihr spezifisches Icon oder als Rechteck mit Label dargestellt werden.
Das Vokabular der UML kann erweitert werden.
Ein neuer selbst definierter Stereotype definiert eine neue Art von Model Element. Die UML ermöglicht damit, fehlende Model Elemente zu definieren.
Z.B. könnte man im Rahmen eines Netzwerkdesigns Symbole für Router erfinden.
Einem Stereotype kann ein eigenes Icon zugewiesen werden, z.B. Skizze eines Routers, eine spezielle Semantik und spezielle Eigenschaften. Gemeint sind hier die Eigenschaften des Model Elementes, wie Name , Attribute und Operationen im Falle einer Klasse.
8 Wie findet man Actors und Use Cases
Man identifiziert zunächst einen Actor und überdenkt dann dessen Interaktion mit dem System.
Wie findet man Actors ?
Man beantworte etwa folgende Fragen :
Wer wird die wesentliche Funktionalität des Systems nutzen ?
Wer wird das System warten und administrieren ?
Welche Hardwaregeräte benötigt das System ?
Mit welchen anderen Systemen wird das zu entwickelnde System interagieren ?
Wer hat ein Interesse an den Werten, die das System produziert ?
Wie findet man Use Cases ?
Für jeden Actor beantworte man etwa folgende Fragen :
Welche Funktionen erwartet der Actor von dem System ? Was muss er selbst dazu beitragen ?
Muss der Actor irgend eine Art von Information lesen, erzeugen, zerstören, modifizieren oder speichern ?
Muss der Actor über irgendwelche Ereignisse im System unterrichtet werden oder muss er das System über etwas informieren ? Welche Funktionalität ist hierzu nötig ?
Kann die tägliche Arbeit des Actors vereinfacht oder effizienter gestaltet werden ?
Welchen Input/Output benötigt das System und von wo kommt dieser ?
Was sind die wesentlichen Probleme mit der aktuellen Implementation des Systems ?
4 Erstellen von Prototypen für User Interfaces
Die Anwender sollten verschiedene User Interfaces ausprobieren können.
1 Beispiel Bibliothek
2 Getränkeautomat
// Einfügen
5 Festlegen der Supplementary Requirements
1 Beispiel Bibliothek
// Einfügen
2 Beispiel Getränkeautomat
Supplementary Requirements
Die Ausgabe der Flasche nach Einwurf des Geldes soll in 90% der Fälle nach 2 Sek erfolgen.
Die Downtime (Automat aus technischen Gründen nicht funktionsfähig) soll unter 1 Tag pro Monat liegen.
80% der Personen sollen den Automaten innerhalb von 10 Sekunden bedienen können.
Vorraussetzung für die Auslieferung ist die Bereitstellung des Flaschengebers 2 Monate vor dem Auslieferungstermin.
6 Festlegen der Supplementary Requirements
1 Beispiel Bibliothek
// Einfügen
2 Beispiel Getränkeautomat
// Einfügen
3 Fragenkatalog für Supplementary Requirements (aus Industrieprojekt für Bildverarbeitung)
1 Auslieferungstermine/Zeitresourcen
Vom Kunden geforderter Zeitplan /Deadline
2 Kostengrenze/Budget/Geldresourcen
…
3 Nationale oder Internationale Regularien
Sicherheitsstandards USA /China / Germany
4 Conditions
Beispiel
Die BV Bibliothek steht am ... zur Verfügung
Hardware muß zur Verfügung stehen
Lieferanten müssen Schnittstellen Definitionen liefern
5 Produktsupport
Schulungen / Einführung beim Kunden – Wie lange? Online Support / Hotline
6 Juristische Anforderungen
Übereinstimmung mit technischen Standards
ISO9000, CMM-Level 3, ANSI standard
7 Technologische Randbedingungen für die Lösung (Solution Constrains)
z.B. Verwendung von NT, MFC für GUI, CanBus usw.
Look and Feel des User Interfaces
Sprache zwischen Deutsch und Englisch umschaltbar (multilingual)
8 Anforderungen bzgl. Datenvolumen
Objekt-Persistenz mittels Datenbanken oder Dateien,
Datenvolumen
9 Zeitanforderungen/Performance
Antwortzeit
Durchsatzraten
10 Anforderungen an die Präzision des Systems
Fehlertoleranz / Präzision
Beispiel: Die Fehlertoleranz für die Tischpositionierung soll unter 10% des Fahrbereichs liegen.
11 Lastanforderungen
Belastung des Systems / Zuverlässigkeit
Z.B. Mehrbenutzerbetrieb mit der maximalen Benutzeranzahl 100.
12 Anforderungen an Fehlertoleranz, Robustheit des Systems
Verfügbarkeit des Systems
13 Anforderungen an die Benutzbarkeit
Ease of Use /GUI
Ease of Learning (Schulungsaufwand)
14 Anforderungen an die technische Sicherheit bzgl. Anwender und collaborierende Hardware
Z.B. Der Roboter darf nie die Grenzen des Moduls überschreiten.
15 Anforderungen an die Datensicherheit
Zugriffsschutz / Passwörter (verschlüsselt)
16 Anforderungen an die Interoperabilität mit anderen Systemen/ Installationsumgebung (Implementation Environment)
Zusammenspiel mit Netzwerk, Hardware.
Referenzen zu den Schnittstellen der collaborierenden Systeme
17 Anforderungen an die Dokumentation (Benutzer+Wartung)
Technische Dokumentation
Benutzeranleitung
18 Anforderungen an die Wartbarkeit
…
19 Anforderungen an die Portabilität
z.B. Software soll leicht von C++ auf Java portierbar sein,
7 Erstellen einer Liste für mögliche Anforderungen
1 Beispiel Bibliothek
Alle Ideen werden aufgelistet und ggf. mit Attributen versehen.
2 Beispiel Getränkeautomat
List of Requirements
[pic]
Alle Ideen werden aufgelistet und ggf. mit Attributen versehen.
8 Erstellen eines Glossary
Das Glossary enthält wichtige Begriffsdefinitionen.
1 Beispiel Bibliothek
Title = identifiziert ein Buch durch dessen ISBN Nummer
item = identifiziert ein Exemplar eines Buches durch die Bibliotheks ID
2 Beispiel Getränkeautomat
Getränkemarke = Identifiziert alle Getränke eines Herstellers mit denselben Inhaltsstoffen
Lieferantennummer = 6stellige Nummer zur Identifikation des Lieferanten
9 Beispiel Bankautomat
[pic]
Textuelle Beschreibung eines Use Case
Bankautomat
ValidateUser
Main flow of events :
Das System bitte den Kunden um seine PIN.
Der Kunde gibt seine PIN über die Tastatur ein.
Der Kunde bestätigt die Eingabe mittels Enter Button.
Das System überprüft die PIN.
Falls die PIN gültig ist, bestätigt das System die Gültigkeit.
Exceptional flow of events :
Der Kunde cancelt die Transaktion über den Cancel Button.
Das System ändert nicht den Account des Kunden.
Exceptional flow of events :
Der Kunde korrigiert seine PIN vor der Betätigung der Enter Taste.
Exceptional flow of events :
Der Kunde gibt eine ungültige PIN ein.
Daraufhin startet die Eingabe erneut. Nach dreimaliger Fehleingabe in Folge wird die gesamte Transaktion abgebrochen.
3 Analyse & Design
1 Identifizierung von Klassen und Objekten
1 Beispiel Bibliothek
// Einfügen
2 Beispiel Getränkeautomat
Entity Objects:
• Geld
• Getränke
• Lieferanten
Boundary Objects:
• Zentraler Monitor
• Geld-Ein-/Ausgeber
• Temperatursensor
Control Objects
• Geldprüfer
• AusgabeManager
3 Grundlegende Probleme
Einige Zitate :
Booch :
„... Der Prozess objektorientierter Analyse und Designs kann nicht in einem Kochbuch beschrieben werden ...“
Booch :
„Wenn wir klassifizieren, versuchen wir Dinge zu gruppieren, die eine gemeinsame Struktur oder ein gemeinsames Verhalten aufweisen.
Es gibt keine einfachen Rezepte, keine "perfekte" Klassenstruktur und nicht die "richtige" Objektmenge. Designentscheidungen stellen immer einen Kompromiss dar, der von vielen gegeneinander abzuwägenden Faktoren abhängig ist.
Der Designer muss z.B. die Wahrscheinlichkeit für Änderungen einschätzen. Solche Abschätzungen basieren auf Erfahrung und erfordern Wissen über den Einsatzbereich der Applikation sowie ein Verständnis für Hardware- u. Softwaretechnologie.
Die besten Software-Designs sehen immer einfach aus.
Intelligente Klassifizierung entsteht am besten in einem inkrementellen und iterativen Prozess.“
Dijkstra
"Die Herausforderung der Programmierung ist eine umfassende Aufgabe angewandter Abstraktion und erfordert deshalb die Fähigkeiten eines Mathematikers, kombiniert mit der Vorgehensweise eines kompetenten Ingenieurs"
Es gibt kein allgemeines Rezept, welches Intelligenz, Erfahrung und guten Geschmack beim Programmieren ersetzt.
Grundprinzipien zur Bewältigung von Komplexität :
Es gibt nur einen grundlegenden Weg zur Bewältigung von Komplexität : Zerlegung (divide et impera teile und herrsche) und Abstraktion.
Da wir meist nicht in der Lage sind, ein komplexes Objekt in seiner Ganzheit zu verstehen, ignorieren wir einfach die unwichtigen Details und beschäftigen uns statt dessen mit dem idealisierten Modell des Objekts. Wir reduzieren damit die Information auf ein für uns erträgliches Maß. Abstraktion bedeutet Informationsreduktion.
Die im Folgenden beschriebenen Vorgehensweisen bieten dennoch eine gewisse Hilfestellung.
4 Studieren des Problem Domains
Welche Klassen, Objekte enthält der Problembereich ?
Die Betrachtung der folgenden Stereotypen ist hilfreich :
1 Entity Class
Diese Klassen repräsentieren reale Objekte aus dem vom System verwalteten Bereich (Business Objects)
Die Entity Klassen repräsentieren meist die logische Strukturierung der Informationen, die das System verwaltet.
Beispiele
Reale Dinge : Bankkunde, Konto, Bücher, Autos, Flugzeuge, Personen, Technische Geräte
Ereignisse : Bestellung, Anfrage, Landung
Orte : Koordinaten, Adressen
Organisationen : Firma, Verein
2 Boundary Class
Ein Objekt der Boundary Class interagiert mit einem Actor ausserhalb des Systems.
Beispiele
Auswahlfenster, Kartenleser, Sensor für die Geldentnahme, Temperatursensor
Man ordnet jedem externen Gerät eine Boundary Class zu.
Man ordnet jedem externen Softwaresystem eine Boundary Class zu.
Man ordnet jedem menschlichen Actor ein Primary Window (Boundary Class) zu.
Man ordnet jedem Entity Object, mit dem der Actor interagiert, ein untergeordnetes Fenster (Boundary Class) zu.
3 Control Class
Die Objekte der Control Class kontrollieren und koordinieren Interaktionen.
Beispiele
BestellungsManager, KreditPrüfer, UserInterfaceDirector
Ein solches Objekt kann z.B. einen einzigen Use Case überwachen und steuern oder eine Menge untergeordneter Entity Objekte managen.
Eine solche Steuerung beinhaltet oft die Überwachung von Geschäftsregeln (Business Logic), die keinem Entity Object zugeordnet werden können.
Man kann etwa jedem Use Case eine Control Class zuordnen. Dies ist allerdings unnötig, wenn der Mensch wesentliche Teile der Steuerung durch Interaktion mit dem System selbst übernimmt oder ein einziges Steuerungsobjekt für alle Use Cases ausreicht.
5 Studium der sprachlichen Beschreibung des Problems
Man studiert die sprachliche Beschreibung der Vision und der Use Cases.
Substantive sind Objektkandidaten.
Verben sind potentielle Elementfunktionen.
Häufig beschreiben Nomen o. Adjektive erforderliche Klassen
(z.B. speicherfähig, gleichzeitig).
Achtung : Verben können substantiviert werden. Sprache ist meist ungenau.
6 Studium existierender Software
Man studiert z.B. das aktuelle Computer System einer Bibliothek oder andere ähnliche Systeme.
Die Verwendung von vorangegangener Arbeit als Inspiration ist eine akzeptable Technik für Innovation.
2 Erstellen eines Class Diagrams
1 Beispiel Getränkeautomat
// Einfügen
2 Beispiel Bibliothek
// Einfügen
3 Ziel
// Einfügen
Klassen können in vielfältigen Beziehungen zueinander stehen.
Z.B. kann eine Klasse eine andere nutzen oder eine Spezialisierung einer anderen Klasse sein.
4 UML Darstellung der Klassen
Klassen werden durch Rechtecke dargestellt.
Attribute und Operationen können optional ausgeblendet werden.
Manche Klassen, wie Prozesse, Threads, Hardware Devices, Applets repräsentieren immer wieder verwendete und wichtige architektonische Abstraktionen. Daher werden sie von UML mit eigenen Stereotypen und Icons unterstützt.
Im Falle von abstrakten Klassen oder abstrakten Operationen wird der Name kursiv (italic) geschrieben.
Eine Class ist ein Spezialfall eines Classifiers.
Classifier unfassen Nodes, Use Cases, Components usw.
+ steht bei Attributen und Operationen für public und – für private und # für protected.
Class Scope Attribute (Statische Variable) werden unterstrichen.
Class Scope Operationen (Statische Operationen) werden unterstrichen.
Klassen Icons können auch verwendet werden, um primitive Typen wie int, char, bool usw. darzustellen.
(UML unterscheidet zwischen Operationen und Methoden.
Eine Operation spezifiziert einen Service, der von jedem Objekt angefordert werden kann. Eine Methode implementiert den Service. In einem Vererbungsgraph existieren mehrere Methoden für dieselbe Operation. Die zum aktuellen Objekt gehörende Methode wird zur Laufzeit ausgewählt.)
5 Identifizieren der Beziehungen zwischen Klassen
1 Association
1 Allgemein
Ein Link repräsentiert eine Verbindung zwischen zwei Objekten, über die eine Message gesendet werden kann (Java Referenz oder C++ Pointer).
Eine Association zwischen Klassen beschreibt eine Menge von Links zwischen Objekten dieser Klassen.
Ein Link ist eine Instanz einer Association.
Eine Association gibt eine semantische Abhängigkeit ansonsten nicht verwandter Klassen an.
Eine Association ist normalerweise bidirektional. D.h. man kann in beide Richtungen navigieren.
Ein Pfeil zeigt an, dass die Association nur in einer Richtung verwendet werden kann. Die Navigierbarkeit wird damit für eine Richtung ausgeschaltet.
Eine Association hat einen oder zwei Namen für jede Richtung. Mit Hilfe dieser Namen kann die Association „gelesen“ werden.
Ferner kann man die Rolle benennen, welche eine Klasse in der Beziehung zu einer anderen übernimmt. Die Rollennamen sind Teil der Association und nicht Teil der Klassen.
Die Multiplicity wird am Ziel einer Association angemerkt und gibt die Anzahl der Links zwischen jeder Instanz der Quellklasse und Instanzen der Zielklasse an.
Recursive Association
Eine Association einer Klasse zu sich selbst wird als Recursive Association bezeichnet.
Association Class
Wenn detailliertere Informationen über einen Link gespeichert werden müssen, bietet sich die Verwendung einer Klasse an. Zu jedem Link der Association wird dann ein Objekt der Association Class erzeugt.
2 Aggregation
Eine Aggregation ist ein Spezialfall der Assiciation und zeigt eine Ganz- /Bestandteil-Beziehung
Nicht immer sind die beiden Klassen an den Enden der Association gleichwertig oder auf demselben Level. Manchmal möchte man ausdrücken, dass die eine Klasse das „Ganze“ repräsentiert und die andere Klasse einen „Teil“.
Schlüsselworte zur Identifizierung und Kennzeichnung einer Aggregation sind "part of" , "has a", „consist of“, „contains“
Z.B. enthält jede Instanz der Klasse Auto eine Instanz der Klasse Motor.
Die Teil/Ganzes Hierarchie muss nicht unbedingt ein physikalisches Beinhalten bedeuten. Z.B. hat ein Verein viele Mitglieder ohne diese physikalisch zu beinhalten.
Auch die Beziehung zwischen dem Vermögen eines Aktionärs u. seinen Anteilen kann als Aggregation betrachtet werden. Das Vermögen enthält Aktien, Anleihen, usw.
Eine Aggregation beschreibt oft verschiedene Abstraktionslevel, wenn man das „Ganze“ ohne seine „Bestandteile“ betrachtet (Reduktion von Information).
Die Realisierung in C++ erfolgt durch Elementobjekt, Zeiger o. Referenz. In Java erfolgt die Realisierung durch Referenz.
Auch das enthaltene Objekt darf einen Zeiger auf das umschließende Objekt enthalten (zyklische Zeigerbeziehung).
3 Composition Aggregation
Eine Composition Aggregation drückt im strengen Sinne Eigentum aus. Die Teile leben und sterben mit dem Ganzen.
Die Multiplicity auf der Seite des Ganzen muss 0 oder 1 sein.
Im Falle der Realisierung durch ein C++ Elementobjekt ist die Erzeugung und Vernichtung automatisch an die umschließende Instanz gekoppelt.
Im Falle eines Zeigers oder einer Referenz sollten die Bestandteile mit dem umschließenden Aggregat-Objekt erzeugt und vernichtet werden.
Die Verantwortlichkeit für die Existenz eines Bestandteils kann durchaus von einer Klasse an eine andere übergeben werden (Shared part class).
???Shared Aggregation
Eine Shared Aggrgation ist ein Spezialfall einer Aggregation.
Personen können z.B. Mitglieder von mehreren Vereinen sein. Man erkennt dies an der Multiplicity auf der Seite des Ganzen.
2 Realization
Eine Klasse realisiert etwa ein Interface, d.h. sie implementiert das im Interface spezifizierte Verhalten.
3 Generalization
Das spezifische Element enthält im Vergleich zum generellen Element zusätzliche Informationen.
Eine Instanz des spezifischen Elementes kann an Stelle einer Instanz des generellen Elementes benutzt werden.
Die spezifische Klasse erbt Attribute, Operationen und Assoziationen.
(Vererbungs-Beziehung "is a")
Generalization kann für Classes, Use Cases und Packages genutzt werden.
???Klassen und Funktionen können durch den Property String {abstract} gekennzeichnet werden.
???Polymorphismus kann nur durch Notes ausgedrückt werden.
???Constraint Generalizations
Overlapping and Disjoint Generalizations
Overlapping Generalization bedeutet, dass Mehrfache Vererbung mit gemeinsamer Basisklasse erlaubt ist. Disjoint Generalization erlaubt keine gemeinsame Basisklasse.
???Complete Generalization
Mittels Constraint kann spezifiziert werden, dass keine weiteren Klassen abgeleitet werden dürfen.
Eine Generalisierung drückt immer unterschiedliche Abstraktionslevel aus.
4 Dependency
Das Element ist von einem anderen abhängig.
Beispiele für Abhängigkeiten :
Operationen der Client Klasse haben eine Signatur mit Returnwert oder Parameter vom Typ der Server Klasse.
Eine Klasse greift auf ein globales Objekt vom Typ einer anderen Klasse zu.
Eine Klasse ruft eine Class Scope Operation einer anderen Klasse.
Eine Elementfunktion einer Klasse definiert ein lokales Objekt einer anderen Klasse.
In allen Fällen muss der Code der abhängigen Klasse modifiziert werden, wenn sich das Interface der verwendeten Klasse ändert.
Diese Beziehung kann auf Classes, Use Cases, Packages angewandt werden.
6 Templates
Ein Template ist eine Klasse, die nicht vollständig spezifiziert wurde. Die endgültige Spezifikation erfolgt über Parameter. Man spricht auch von einer parametrisierten Klasse.
Eine parametrisierte Klasse kann ein Array sein. Die instanzierte Klasse kann dann ein Array von Autos oder ein Array von Farben sein.
3 Weitere Beispiele
Buch S. 45 Bild 3.5
Buch S. 47 Bild 3.6 + Text
Text S. 47 unten
Buch S. 185 Bild 8.8
Buch S. 188 Bild 8.12 + Text S. 189
Unterschied zwischen dem Requirement Domain Model und dem Analyse Model nach Rational :
Die Klassen des Domain Models repräsentieren reale Objekte
Die Klassen des Analyse Models repräsentieren Objekte des Computersystems
4 Erstellen der Use Case Realisierungen
1 Beispiel Getränkeautomat
// Einfügen
2 Beispiel Bibliothek
// Einfügen
3 Erstellen eines Use Case spezifischen Class Diagrams
Das Use Case spezifische Class Diagram zeigt alle an dem Use Case partizipierende Klassen.
4 Erstellen eines Object Diagrams
1 Konzept
Ein Object Diagram ist eine Abwandlung des Klassendiagramms.
Im Unterschied zum Klassendiagramm zeigt es die Instanzen der Klassen und Instanzen der Associations.
In diesem Sinne zeigt das Object Diagram einen Schnappschuss des Systems zu einem bestimmten Zeitpunkt während der Ausführung.
Das Object Diagram nutzt mit zwei Ausnahmen dieselben Notationen wie das Class Diagram. Die Namen der Objekte werden unterstrichen und es werden alle Instanzen einer Beziehung dargestellt.
Durch eine geeignet gewählte Folge von Zeitpunkten kann man daher die Statusänderungen eines Objektes darstellen. Der Status eines Objektes umfasst alle Attribute und die aggregierten Teile.
Der Klassenname kann weggelassen werden und es können unbenannte Objekte einer Klasse dargestellt werden.
2 Associations
Die Instanzen der Associations werden als Links bezeichnet. Ein Link spezifiziert ein Pfad entlang dem eine Message gesendet werden kann.
Links können mit Stereotypen versehen werden.
UML erlaubt für fast alle Model Elemente Typen und Instanzen darzustellen.
Die Sichtbarkeit der Objekte kann näher beschrieben werden :
Association
Das korrespondierende Objekt ist sichtbar, weil eine Association bzgl. der Klassen existiert.
(Das sind die wichtigsten Links, da sie die Struktur des Systems widerspiegeln !)
Parameter
Das korrespondierende Objekt ist sichtbar, weil es ein Parameter ist.
Local
Das korrespondierende Objekt ist sichtbar, weil es eine lokale Variable ist.
Global
Das korrespondierende Objekt ist sichtbar, weil es eine globale Variable ist.
Aktive Objekte, das sind Objekte, welche die Wurzel eines Kontrollflusses (Thread) repräsentieren, können durch einen fetten Rand markiert werden oder durch den Stereotyp .
5 Erstellen eines Collaboration Diagram
Das Collaboration Diagram zeigt den Nachrichtenaustausch im Kontext der Objekte und deren Beziehungen.
Das Zusammenspiel ist eine grundlegende Designentscheidung !
Ein Objekt erhält eine Botschaft und sendet daraufhin eine Botschaft an sich selbst (Aufruf eigener Elementfunktion) bzw. an ein kooperierendes Objekt (Aufruf Elementfunktion von kooperierendem Objekt).
Das Wissen über das Zusammenspiel verschiedener Objekte ist in keiner der beteiligten Klassen vollständig vorhanden. Der Mechanismus des Zusammenspiels ist ein Muster (Pattern), dessen Wiederverwendbarkeit meist höher ist als die einzelner Klassen.
Innerhalb der Szenarien sollte man daher nach nicht trivialen Mechanismen suchen, die im Rahmen der Systemarchitektur dokumentiert werden können (Beschreibung der Mechanismen durch Sequence und Collaboration Diagrams).
Üblicherweise werden unterschiedliche Ablaufmöglichkeiten in verschiedenen Collaboration Diagrams darsgestellt.
Jedem Link kann eine Message mit einer Nummer zugeordnet werden.
(Die Verschachtelung der Nachrichten kann durch eine Nummerierung analog zur Kapitelnummerierung ausgedrückt werden. 1.2 ist z.B. die zweite Message innerhalb der Behandlung der Message 1.)
Die Erzeugung bzw. Vernichtung eines Objektes kann mittels new/create bzw. destroy/delete Messages oder Konstruktor-/Destruktoraufruf gekennzeichnet werden.
Kontrollflüse können unterschieden werden, indem man dem Funktionaufruf den Namen des Threads voranstellt.
6 Erstellen eines Sequence Diagrams
Ein Sequence Diagram zeigt den Nachrichtenaustausch zwischen den Objekten zeitlich auseinandergezogen entlang der vertikalen Lebenslinie der Objekte.
Ein Sequence Diagram betont die Zeit (Focus : Time) und die Reihenfolge, ein Collaboration Diagram betont die Objekte und deren Beziehungen (Focus : Space).
Sie repräsentiert die Existenz des Objektes zu einem bestimmten Zeitpunkt.
Über die Lebenslinie lässt sich die Erzeugung oder Vernichtung eines Objektes darstellen.
Der Empfang einer Nachricht aktiviert das empfangende Objekt. Ein aktiviertes Objekt führt entweder seinen eigenen Code aus oder wartet auf die Antwort eines anderen Objektes, dem es eine Nachricht gesendet hat. Die Aktivität eines Objektes wird durch ein vertikales dünnes Rechteck dargestellt.
Man unterscheidet prinzipiell zwischen der Generic Form und der Instance Form eines Sequence Diagrams.
Die Instance Form beschreibt ein spezifisches Scenario im Detail. Sie dokumentiert eine mögliche Interaktion. Es gibt keine Bedingungen, Verzweigungen oder Schleifen.
Die Generic Form beschreibt alle möglichen Alternativen in einem Scenario. Daher existieren Verzweigungen, Bedingungen und Schleifen.
Iterationen lassen sich durch eine Bemerkung am Rand ausdrücken.
Man kann auch die Message mit einem Prefix versehen, welches die Iteration [i := 1..n] oder die Bedingung [x > 0] für eine Verzweigung beschreibt. UML schreibt das Format nicht vor.
Üblicherweise stellt man die verschiedenen Ablaufmöglichkeiten jedoch nicht in einem einzigen Sequenzdiagramm sondern in unterschiedlichen Sequenzdiagrammen dar.
Messages können eine Signatur mit Parameter haben.
Messages können Nummern haben.
Man unterscheidet folgende Message Types
Simple
For messages with a single thread of control, one object sends a message to a passive object.
Synchronous
In synchronous messages, the operation proceeds only when the client sends a message to the supplier and the supplier accepts the message. The client runs until it sends the message; it then waits for the supplier to accept it. The client continues to wait until the message is accepted.
Asynchronous
Asynchronous communication occurs when the client sends a message to the supplier for processing and continues to execute its code without waiting for or relying on the supplier's receipt of the message.
5 Spezifizierung der Klassen
1 Beispiel Getränkeautomat
[pic]
Ausgabe Manager
Diese Klasse ist verantwortlich für die komplette Steuerung einer Flaschenausgabe. Sie beinhaltet die für den Verkauf erforderliche Geschäftslogik. Insbesondere prüft sie, ob der eingegebene Geldbetrag für die selektierte Flasche ausreichend ist. Die Verbuchung von Geld und Flasche sowie die eigentliche Ausgabe delegiert sie an die entsprechenden Klassen.
Die Klasse ist passiv.
Es gibt nur eine Instanz dieser Klasse.
Funktionsbeschreibungen:
...
...
...
2 Beispiel Bibliothek
// Einfügen
3 Identifizieren von Elementfunktionen und Attributen
Wie findet man Elementfunktionen und Attribute ?
Man betrachtet die Use Cases sowie die identifizierten Klassen und analysiert des Zusammenspiel der Objekte mittels Sequence- und Collaboration Diagrams.
Aus den an die Instanzen der Klasse gesendeten Botschaften ergibt sich die öffentliche Schnittstelle der Klasse.
Diese Funktionen werden durch Konstruktoren, Destruktoren und private Hilfsfunktionen ergänzt.
Man notiert die zur Ausführung einer Funktion erforderlichen Informationen. Auf diese Weise ergeben sich Klassenattribute und Funktionsparameter.
Man analysiert die möglichen Zustände der Bestandteile durch Betrachtung von State Diagrams (vgl. weiter unten).
Diese führen zu States in Form von Attributwerten und zu Events und Actions in Form von Operationen.
Im Rahmen des Designs sollten auch die Parameter der Operationen präzisiert werden. Dabei ergeben sich wiederum neue Klassen zur „Verpackung“ von Informationen für einen Transport.
Anmerkungen
Formalisierte Sprachen für den Entwurf
Analyse und Entwurf sind genau so fehleranfällig wie die Implementation. Sie sind ggf. sogar fehleranfälliger, da sie weniger präzise (meist keine formale Sprache) spezifiziert und nicht wie die Implementation durch Werkzeuge überprüft werden können (z.B. Compiler).
Durch eine strenge Formalisierung der Notation/Sprache kann hier eine Verbesserung erzielt werden (z.B. Entwurfsformulierung mit DLCA Defined Language of Cooperating Automata). Jedoch muß der Formalismus dem praktischen Problem angemessen sein und er darf den mathematischen Background der beteiligten Designer und Programmierer nicht übersteigen
Manchmal ist es sinnvoll, logisch zusammengehörende und in vielen Objekten benutzte Hilfsfunktionen in einer eigenen Service-Klasse (ohne Daten !) zusammenzufassen, z.B. Funktionen zum Sortieren oder zur Umrechnung vom Dezimal- ins Hexadezimalsystem.
Man schreibe grundsätzlich Elementfunktionen für Konstruktion u. Destruktion (auch Copy-Konstruktor !) und Zuweisungsoperatoren (operator=()).
Man schreibe ggf. Vergleichsoperatoren (operator==(), operator!=()).
Funktionen zum Auslesen- bzw. Setzen der Zustandsdaten eines Objektes (Selektor bzw. Modifizierer).
Enthält ein Objekt viele Elementobjekte vom gleichen Typ, benötigt man einen Iterator, der es erlaubt, auf alle Elementobjekte in einer wohdefinierten Reihenfolge zuzugreifen.
Für den Zugriff auf die Daten sind prinzipiell Funktionen zu benutzen.
Ist die Menge der über die Funktionsschnittstelle zu transportierenden Daten zu groß, sollte trotzdem kein direkter Zugriff auf die Daten gewährt werden. Stattdessen sollte man Iteratoren zur Verfügung stellen, die es ermöglichen, sich in der Menge zu bewegen. Ferner werden dann Funktionen zum Einfügen, Löschen, Editieren benötigt.
Wenn man davon ausgeht, daß die Daten in einem nicht zugreifbaren Speicher (z.B. Datenbank) verwaltet werden, können keine Zeiger auf die zu editierenden Daten übergeben werden. Das Editieren ist nur möglich, indem man die Daten als Parameter übergibt oder vor dem edit() Aufruf eine im eigenen Speicher zugreifbare Kopie editiert (Konzept von CRecordset).
Die Kopie sollte Teil des Objektes sein, welches die Datenmenge repräsentiert.
Kommunikation zwischen Ansichten und Dokumentenobjekten sollte nicht über den "Dienstweg" erfolgen (Objektbaum hoch und wieder runter). Man müßte sonst im obersten Objekt (Dokument) sämtliche Nachrichten an die untergeordneten Objekte (z.B. Testfall) weitergeben können. D.h. aber, daß alle Schnittstellenelementfunktionen der untern Objekte auch Schnittstellenlementfunktionen des obersten Objektes sein müssen, da Nachrichten nur über Elementfunktionen ausgetauscht werden können. Daher baut man besser Assoziationen zwischen untergeordneten Objekten auf (z.B. könnte das Testfallansichtsobjekt einen Zeiger auf das aktuelle Testfalldatenobjekt erhalten) und ermöglicht so den direkten Nachrichtenaustausch.
Wir ergänzen private nicht in der Schnittstelle enthaltene Hilfsfunktionen, wie etwa Prüfroutinen (index im erlaubten Bereich, auto in der gewünschten Farbe darstellbar usw.) , Funktionen zur Vereinfachung der Notation (gebeDatumAus()).
Wir unterscheiden ggf zwischen allgemeinen Anwendern einer Klasse und erbenden Anwendern durch spezifizieren von protected Anteilen an der öffentlichen Schnittstelle.
Wir legen die Argumenttypen der Schnittstellenfunktionen fest.
Dabei ist darauf zu achten, daß alle Operationen einer Klasse dasselbe Abstraktionsniveau unterstützen und daß dieses zum Abstraktionsniveau der Klasse paßt.
Beispiele für typische Entwurfsentscheidungen :
Soll z.B. in einer Klasse cZylinder das Volumen gespeichert o. jeweils berechnet werden.
Sollen z.B. Flugplaninformationen bzgl. der Suche oder dem Löschen/Einfügen optimiert werden.
Implementierung von Statusmaschinen durch Darstellung jedes Status durch ein Objekt einer den Status repräsentierenden Klasse. Diese Objekte verhalten sich bedingt durch Polymorhie unterschiedlich und lösen damit unterschiedliche Aktionen aus.
4 Beschreibung der Verantwortlichkeiten der Klassen
Die Bedeutung der im Diagramm dargestellten Klassen muss für den Leser deutlich werden. Daher ist die Verantwortlichkeit jeder Klasse textuell durch einige Sätze zu beschreiben.
Die grobe Verantwortlichkeit einer Klasse kann bereits durch den Namen deutlich werden, wenn ein entsprechender Klassenbegriff in der Umgangssprache vorliegt.
Zusätzlich müssen charakteristische Eigenschaften verbal oder durch die Angabe von einigen Attributen oder Elementfunktionen (Schnittstellenfunktionen, keine privaten Hilfsfunktionen) beschrieben werden.
Ferner wird im Rahmen der Klassenspezifikation angegeben :
Basisklassen
Zustandsdiagramm (vgl. weiter hinten)
Anzahl der möglichen Instanzen
Multithreading Verhalten
Aktive Klasse
5 Erstellen der Beschreibungen der Schnittstellenfunktion
Die Funktionsbeschreibungen kann man z.B. untergliedern nach
Konstruktoren,
Destruktoren
Gruppen von public Functions
Safe&Load Functions
Die Funktionsbeschreibung einer Methode sollte enthalten
Funktionskopf mit Signatur
Return Value
Parameters
Exceptions
Specifier const, virtual, abstract
Multithreading Verhalten
Remarks
Im Rahmen der Properties für eine Operation können z.B. darüber hinaus Preconditions, Postconditions oder Zustandsänderungen formuliert werden.
Wenn die Parameter der Elementfunktionen bereits spezifiziert sind, sollte man nach Gemeinsamkeiten suchen. Ggf. kann man gemeinsame Klassen für gewisse Parameter einführen.
Im Falle von Klassenkategorien stellen die Operationen die Services dar, die von der Kategorie exportiert werden und die letztendlich von vielen Klassen der Kategorie realisiert werden.
Der Unterschied zwischen Verhalten (Funktion) und Eigenschaft (Attribut) ist oft nur schwer erkennbar. Z.B. kann man die Tatsache, daß ein Lkw beladen werden kann durch ein Attribut "LadungInTonnen" oder eine Elementfunktion laden() ausdrücken.
Beispiel für das Zusammenspiel verschiedener Objekte :
Im Rahmen eines Updates wird jede Ansicht aufgefordert, die Daten darzustellen.
Jede Ansicht erfragt die Daten des Dokumentes, bildet ein grafisches Modell und fordert dieses auf sich zu zeichnen.
Document und View liegen auf demselben Level. Es handelt sich hier also nicht um Aufträge an ein untergeordnetes Objekt.
6 Updaten der Beziehungen zwischen den Klassen
Einrichten von Vererbungsbeziehungen
Werden z.B. Operationen oder Attribute von mehreren gleichberechtigten Klassen benötigt, sollten sie in einer ggf. abstrakten Oberklasse untergebracht werden.
An dieser Stelle findet ein Teil der Klassifizierung statt.
Einrichten der Associations
Die für den Botschaftsverkehr notwendigen Assoziationen müssen eingerichtet werden.
Diese Associations werden dann ggf. zu Aggregations oder Composite Aggregations verfeinert.
Es werden Rollen, eventueller Einschränkungen und Kardinalitäten notiert.
Achtung : Vererbungsbeziehungen sind keine Botschaftswege (Basis- u. abgeleitete Klasse stehen nicht für verschiedene Objekte).
7 Statechart Diagram
1 Beispiel Getränkeautomat
// Einfügen
2 Beispiel Bibliothek
// Einfügen
3 Konzept
Ein Statechart-Diagram zeigt alle möglichen Zustände eines Objektes und welche Ereignisse zustandsänderungen bewirken.
4 States
Alle Objekte haben einen Status. Dieser ist bestimmt durch die Werte der Attribute (einschließlich der Links zu anderen Objekten). Jede Änderung eines Attributwertes ist im Prinzip eine Statusänderung.
State Diagrams werden nur für solche Klassen gezeichnet, die eine gewisse Anzahl wohldefinierter Zustände besitzen, welche das Verhalten der Klasse beeinflussen.
Es gibt einen Initial State und ggf. mehrere Final States.
In UML besitzt jeder State einen Namen.
Optional können Attributwerte, Events und Actions angegeben werden.
Durch Schachtelung können Substates dargestellt werden. Der umfassende State wird als Composite State bezeichnet.
Mittels History Indicator können Substates gespeichert werden.
5 Events
Events sind Trigger, die Zustandsübergänge aktivieren.
Der Empfang eines Signals, welches selbst ein Objekt ist oder der Aufruf einer Operation durch ein anderes Objekt wird durch eine Event-Signature an einer Transition dargestellt.
Events können mit Parameter versehen werden (Event Signature).
Der Ablauf einer Zeitperiode wird durch einen Zeitausdruck an einer Transition gekennzeichnet.
6 Transitions
Die Zustandsänderung wird als Transition bezeichnet.
Einer State Transition ist normalerweise ein Event zugeordnet. In diesem Falle wird die Transition bein Auftreten des Events durchgeführt.
Wenn kein Event zugeordnet ist, wird die Transition ausgeführt, wenn die internen Actions des Source States beendet sind.
Guard Condition
Eine Guard Condition ist ein Boolscher Ausdruck. Eine Transition, welche mit diesem Ausdruck versehen ist, „feuert“, wenn der Ausdruck wahr ist.
Falls eine Transition mit Guard Condition und Event gekennzeichnet ist, muss der Ausdruck wahr und der Event eingetreten sein.
Action Expression
Mittels Action Expressions können Transitions mit Operationen oder Attributzuweisungen versehen werden.
Für Wertzuweisungen wird := verwendet.
Die Anweisungen sind durch / getrennt.
Eine Action ist eine ausführbare atomare Berechnung. Hierunter fällt z.B. der Aufruf einer Funktion, die Erzeugung eines Objektes oder das Senden eines Signals an ein anderes Objekt. Atomar bedeutet, dass die Berechnung nicht durch einen Event unterbrochen werden kann.
Innerhalb eines States kann mittels der Standardevents Entry, Exit spezifiziert werden, welche Actions jeweils beim Eintritt in den Zustand und beim Austritt aus dem Zustand durchzuführen sind.
Die Spezifizierung einer Action mittels Entry erspart es, jede Transition, die in den betreffenden Zustand führt, mit dieser Action zu kennzeichnen.
8 Activity Diagram
(Eriksson S. 23 Bild 2.8)
1 Beispiel Getränkeautomat
// Einfügen
2 Konzept
Eine Activity besteht aus einer Sequenz von Actions und ist durch einen Event unterbrechbar.
Eine Activity kann z.B. eine längere Berechnung oder das Pollen einer Queue sein.
Ein Statchart Diagram betont die Zustände eines Objektes und zeigt die mit den Zustandsübergängen und Zuständen verbundenen Aktivitäten nur nebenbei.
Ein Activity Diagram betont hingegen die Aktivitäten.
Man kann ein Activity Diagramm als Spezialfall eines Statchart Diagrams ansehen, in welchem alle Aktivität gewissen States zugeordnet sind und alle Transitions durch die Beendigung der Aktivität getriggert werden.
Activity Diagrams zeigen den Kontrollfluss von Aktivität zu Aktivität (Focus : Work).
9 Identifizierung aktiver Klassen
Falls z.B. einer der Actors hohe Performance Ansprüche hat, sollte er durch ein eigenes aktives Objekt bedient werden.
Eine Verteilung auf mehrere Rechner erfordert zumindest ein aktives Objekt pro Rechner und separate aktive Objekte für die Rechnerkommunikation.
In diesem Zusammenhang muss dann z.B. die Kommunikation der aktiven Objekte, die Synchronisartion, die Vermeidung von Deadlocks und Starvation sowie die Erzeugung und Terminierung bedacht werden (vgl. Multithreading Vorlesung SE2).
6 Erstellen eines Package Diagrams
1 Beispiel Getränkeautomat
[pic]
2 Beispiel Bibliothek
[pic]
3 Packages
Bei allen hinreichend großen Systemen können wir Klassengruppen erkennen, die in sich selbst stark aber mit anderen Gruppen nur lose gekoppelt sind, die semantisch eng zusammen gehören und meist nur gemeinsam geändert werden können.
Z.B. könnte man die Java Oberfläche durch eine Windows-Oberfläche ersetzen wollen.
Daher gruppieren wir z.B. alle Oberflächen-Klassen in einem eigenen Package.
Packages dienen dazu, das logische Modell eines Systemes aufzuteilen.
Ein Package ist ein Mechanismus zum Gruppieren.
Im Hinblick auf den begrenzten Überblick eines Menschen sollte die Anzahl der in einem Package zusammenarbeitenden Klassen oder Packages nicht deutlich über 7 liegen. Experimente des Psychologen Miller stützen dies (Begründung duch Leistungsvermögen Kurzzeitgedächtnis).
4 Beziehungen zwischen Packages
Packages können in einer Dependency, Genaralization oder ???Refinement Beziehung zueinander stehen.
Die Abhängigkeit drückt bei Packages die Importbeziehung aus. Die Klassen der im "Importbaum" obenliegenden Packages importieren Klassen der darunterliegenden Packages.
Vererbung hat etwa dieselbe Bedeutung wie bei Klassen. Z.B. spezialisiert das Package WindowsGUI das Package GUI. Das spezialisierte Package erbt damit die public und protected Elemente des verallgemeinerten Packages. Wie bei Klassen kann das spezialisierte Package dabei Elemente ersetzen und neue hinzufügen. Ein spezialisiertes Package kann überall dort verwendet werden, wo ein allgemeines Package erwartet wird.
Ein Package besitzt seine Model Elemente. Ein Model Element kann nicht in mehr als einem Package enthalten sein. Packages können jedoch Model Elements von anderen Packages importieren.
5 Zugriffssteuerung
Der Zugriff auf den Inhalt eines Package kann durch private, protected, public und implementation gekennzeichnet werden.
Im Falle von protected können nur Kinder des Packages das Model Element sehen. Private Elemente sind außerhalb des Packages nicht sichtbar.
Damit hat man eine klare Trennung zwischen den exportierten public Klassen, die das Interface des Package stellen, und den private Klassen, die die Services implementieren.
Exportierte Elemente sind nur in solchen Packages sichtbar, die das exportierende Package explizit importieren.
Die Importbeziehung ist nicht transitiv.
Wenn ein Element in einem Package sichtbar ist, so ist es in allen eingeschachtelten Packages sichtbar. Eingeschachtelte Packages sehen alles, was das umgebende Package sieht.
Ein Package stellt einen Namensraum dar.
Verschiedene Arten von Model Elementen können denselben Namen haben, jedoch darf es z.B. keine zwei Klassen mit demselben Namen geben.
7 Erstellen des Deployment Diagrams
1 Beispiel Getränkeautomat
[pic]
2 Beispiel Bibliothek
[pic]
3 Konzept
Deployment Diagrams zeigen die physikalische Verteilung der Hard- und Software des Systems zusammen mit deren Verbindungen.
Es wird erkennbar, welche Software Component (vgl. weiter unten) auf welchem Gerät ausgeführt wird.
Nodes
Nodes sind physikalische Objekte wie Computer, Drucker, Kartenleser usw.
Sie repräsentieren die Hardware, auf welchen die Components ausgeführt werden.
Ein Node kann als Typ (Klasse) und als Instanz (Objekt) dargestellt werden. Die Klasse beschreibt etwa die Charakteristik eines Processors und das Objekt repräsentiert einen konkreten Processor dieses Typs. Die Eigenschaften (Prozessorgeschwindigkeit oder der vorhandene Speicher) können im Rahmen der Klassenattribute oder der Properties beschrieben werden.
Im Falle von Klassen, können auch Vererbungsbeziehungen dargestellt werden.
Im Falle einer Instanz wird der Name unterstrichen.
Communication Association
Nodes werden mittels Communication Association miteinander verbunden.
Mittels Stereotyp wird das Netzwerk oder das Protokoll gekennzeichnet.
Executable Components
Die Instanzen der ausgeführten Programme können in den Instanzen der Nodes aufgeführt werden.
Objects
Instanzen von Klassen, also Objects, können Instanzen von Nodes zugeordnet werden. Z.B. kann ein Passive Object innerhalb eines Active Objects und dieses innerhalb einer Executable Component dargestellt werden.
8 Unterschied zwischen Analyse und Design
1 Analyse
Booch
„Unsere Erfahrung in der Entwicklung großer Systeme hat gezeigt, dass die zu Anfang erfolgte Spezifizierung der Anforderungen niemals vollständig ist, oft nur ungenau und immer im Widerspruch zu sich selbst steht.“
OOA nach Booch :
„Die objektorientierte Analyse ist eine Analysemethode, welche die Anforderungen aus der Perspektive der Klassen und Objekte, die sich im Vokabular des Problembereichs finden, betrachtet.“
Die Analyse beschreibt, "was" das System tut, nicht "wie" es das tut.
Hierzu gehört die Identifikation von wichtigen Klassen und Objekten des Problembereichs und die Beschreibung der UseCases aus der Sicht der kollaborierenden Objekte.
Das bisher monolithische System splittet auf in Analysis Level Objekte.
Damit wird auch das Problem eingegrenzt, indem festgelegt wird, was von Interesse ist und was nicht.
Das Analysis Model ist noch frei von technischen Details. Es vermittelt daher eine sehr abstrakte Sichtweise der Zerlegung des Systems.
Das Analysis Model liefert ein genaues Verständnis der Anforderungen.
2 Design
Das Design Model beschreibt, "wie" das System die gestellten Anforderungen erfüllt.
In Rahmen des Design treffen Architekten die grundlegenden architektonischen und strategischen Entscheidungen.
Sie eignen sich dabei ein Verständnis der verwendeten Technologien wie Betriebssystem, GUI, Datenbank usw. an.
Sie zerlegen das System in kleinere von ggf. unterschiedlichen Teams bearbeitbare Teile und definieren die Interfaces.
Die UML Definition der Architektur
Architecture is the organizational structure of a system. An architecture can be recursively decomposed into parts that interact through interfaces, relationships that connect parts, and constraints for assembling parts.
Das Analysis Model liefert eine gewisse dem Problembereich angepasste Grundstruktur. Diese meist an den realen Objekten orientierte Grundstruktur sollte man im Design Model nach Möglichkeit beibehalten.
9 Weitere Beispiele
4 Implementation
1 Erstellen des Component Diagrams
1 Beispiel Getränkeautomat
[pic]
2 Beispiel Bibliothek
// Einfügen
3 Konzept
Ein Component Diagram stellt die physikalische Realisierung von logischen Modellelementen und ihre Abhängigkeiten dar.
Es zeigt damit die physikalische Struktur des Codes.
Eine Dependency Relation zwischen Components drückt aus, dass eine Component die andere für ihre vollständige Definition benötigt. In einer compilierten Sprache erfordert die Modifikation der einen Component die Recompilierung der anderen Component.
Im Falle einer Executable Component kann die Dependency Relation verwendet werden, um auszudrücken, welche DLL das Programm benötigt.
Components sind Typen. Nur Executable Components können Instanzen haben. Eine solche Instanz wird auf einem Processor ausgeführt.
Mittels Stereotyp kennzeichnet man die Components genauer, z.B. :
Source Code File
Web Seite
Dokument (nicht compilierbar)
Binary Component
Object Code als Resultat des Compilierens.
Executable Component
Ein ausführbares File als Resultat des Linkens.
Sie repräsentiert die ausführbare Einheit, die auf einem Processor läuft.
Ausführbares Programm
Static or dynamic object library
Datenbank
2 Erstellen des Integration Build Plan
1 Beispiel Getraenkeautomat
[pic]
2 Bottom Up Integration
Man identifiziert diejenigen Subsystems, die an den zu implementierenden Use Cases partizipieren.
[pic]
Man definiert “Build Sets”, das sind Subsystems, die aus Integrationssicht zusammengehören.
[pic]
Man definiert eine Serie von Builds (typischerweise Bottom Up)
[pic]
// Im Beispiel Einfügen
3 Implementieren der Components
1 Ziel
Es werden alle Components produziert, die für ein ausführbares System erforderlich sind.
Insbesondere werden die Design Klassen in File Components mit Source Code und schließlich in Binärfiles bzw. Executables umgesetzt.
Es werden zu den Packages korrespondierende Verzeichnisse angelegt.
2 Programmierregeln
1 Java Code Conventions (Selbststudium)
Vgl. Java-Codeconvention.pdf
Java Code Conventions
1 - Introduction
1.1 Why Have Code Conventions
Code conventions are important to programmers for a number of reasons:
• 80% of the lifetime cost of a piece of software goes to maintenance.
• Hardly any software is maintained for its whole life by the original author.
• Code conventions improve the readability of the software, allowing engineers to
understand new code more quickly and thoroughly.
• If you ship your source code as a product, you need to make sure it is as well packaged
and clean as any other product you create.
For the conventions to work, every person writing software must conform to the code
conventions. Everyone.
1.2 Acknowledgments
This document reflects the Java language coding standards presented in the Java Language
Specification, from Sun Microsystems, Inc. Major contributions are from Peter King, Patrick
Naughton, Mike DeMoney, Jonni Kanerva, Kathy Walrath, and Scott Hommel.
This document is maintained by Scott Hommel. Comments should be sent to
shommel@eng.
2 - File Names
This section lists commonly used file suffixes and names.
2.1 File Suffixes
Java Software uses the following file suffixes:
|File Type |Suffixes |
|Java source |.java |
|Java bytecode |Java bytecode |
2.2 Common File Names
Frequently used file names include:
File Name Use
GNUmakefile The preferred name for makefiles. We use gnumake to build our software.
README The preferred name for the file that summarizes the contents of a particular directory.
3 - File Organization
A file consists of sections that should be separated by blank lines and an optional comment
identifying each section.
Files longer than 2000 lines are cumbersome and should be avoided.
For an example of a Java program properly formatted, see “Java Source File Example” on page
18.
3.1 Java Source Files
Each Java source file contains a single public class or interface. When private classes and
interfaces are associated with a public class, you can put them in the same source file as the
public class. The public class should be the first class or interface in the file.
Java source files have the following ordering:
• Beginning comments (see “Beginning Comments” on page 2)
• Package and Import statements
• Class and interface declarations (see “Class and Interface Declarations” on page 3)
3.1.3 Class and Interface Declarations
All source files should begin with a c-style comment that lists the class name, version
information, date, and copyright notice:
/*
* Classname
*
* Version information
*
* Date
*
* Copyright notice
*/
3.1.2 Package and Import Statements
The first non-comment line of most Java source files is a package statement. After that,
import statements can follow. For example:
package java.awt;
import java.awt.peer.CanvasPeer;
3.1.3 Class and Interface Declarations
The following table describes the parts of a class or interface declaration, in the order that they
should appear. See “Java Source File Example” on page 18 for an example that includes
comments.
|Part of Class/Interface |Notes |
|Declaration | |
|Class/interface documentation |See “Documentation Comments” on page 8 for |
|comment (/**...*/) |information on what should be in this comment. |
|class or interface statement | |
|Class/interface implementation |This comment should contain any class-wide or |
|comment (/*...*/), if necessary |interface-wide information that wasn’t appropri-ate |
| |for the class/interface documentation com-ment. |
|Class (static) variables |First the public class variables, then the pro-tected, then package level (no access |
| |modifier), |
| |and then the private. |
|Instance variables. |First public, then protected, then package |
| |level (no access modifier), and then private |
|Constructors | |
|Methods |These methods should be grouped by functional-ity |
| |rather than by scope or accessibility. For example, a private class method can be in |
| |between two public instance methods. The goal is to make reading and understanding the |
| |code eas-ier. |
4 - Indentation
Four spaces should be used as the unit of indentation. The exact construction of the indentation
(spaces vs. tabs) is unspecified. Tabs must be set exactly every 8 spaces (not 4).
4.1 Line Length
Avoid lines longer than 80 characters, since they’re not handled well by many terminals and
tools.
Note: Examples for use in documentation should have a shorter line length—generally no
more than 70 characters.
4.2 Wrapping Lines
When an expression will not fit on a single line, break it according to these general principles:
Break after a comma.
Break before an operator.
Prefer higher-level breaks to lower-level breaks.
Align the new line with the beginning of the expression at the same level on the previous line.
If the above rules lead to confusing code or to code that’s squished up against the right margin, just indent 8 spaces instead.
Here are some examples of breaking method calls:
someMethod(longExpression1, longExpression2, longExpression3,longExpression4, longExpression5);
var = someMethod1(longExpression1,
someMethod2(longExpression2,
longExpression3));
Following are two examples of breaking an arithmetic expression. The first is preferred, since
the break occurs outside the parenthesized expression, which is at a higher level.
longName1 = longName2 * (longName3 + longName4 - longName5)
+ 4 * longname6; // PREFER
longName1 = longName2 * (longName3 + longName4
- longName5) + 4 * longname6; // AVOID
Following are two examples of indenting method declarations. The first is the conventional
case. The second would shift the second and third lines to the far right if it used conventional
indentation, so instead it indents only 8 spaces.
//CONVENTIONAL INDENTATION
someMethod(int anArg, Object anotherArg, String yetAnotherArg,
Object andStillAnother) {
...
}
//INDENT 8 SPACES TO AVOID VERY DEEP INDENTS
private static synchronized horkingLongMethodName(int anArg,
Object anotherArg, String yetAnotherArg,
Object andStillAnother) {
...
}
Line wrapping for if statements should generally use the 8-space rule, since conventional (4
space) indentation makes seeing the body difficult. For example:
//DON’T USE THIS INDENTATION
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) { //BAD WRAPS
doSomethingAboutIt(); //MAKE THIS LINE EASY TO MISS
}
//USE THIS INDENTATION INSTEAD
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
}
//OR USE THIS
if ((condition1 && condition2) || (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
}
Here are three acceptable ways to format ternary expressions:
alpha = (aLongBooleanExpression) ? beta : gamma;
alpha = (aLongBooleanExpression) ? beta
: gamma;
alpha = (aLongBooleanExpression)
? beta
: gamma;
5 - Comments
Java programs can have two kinds of comments: implementation comments and
documentation comments. Implementation comments are those found in C++, which are
delimited by /*...*/, and //. Documentation comments (known as “doc comments”) are
Java-only, and are delimited by /**...*/. Doc comments can be extracted to HTML files
using the javadoc tool.
Implementation comments are means for commenting out code or for comments about the
particular implementation. Doc comments are meant to describe the specification of the code,
from an implementation-free perspective to be read by developers who might not necessarily
have the source code at hand.
Comments should be used to give overviews of code and provide additional information that is
not readily available in the code itself. Comments should contain only information that is
relevant to reading and understanding the program. For example, information about how the
corresponding package is built or in what directory it resides should not be included as a
comment.
Discussion of nontrivial or nonobvious design decisions is appropriate, but avoid duplicating
information that is present in (and clear from) the code. It is too easy for redundant comments
to get out of date. In general, avoid any comments that are likely to get out of date as the code
evolves.
Note: The frequency of comments sometimes reflects poor quality of code. When you feel
compelled to add a comment, consider rewriting the code to make it clearer.
Comments should not be enclosed in large boxes drawn with asterisks or other characters.
Comments should never include special characters such as form-feed and backspace.
5.1 Implementation Comment Formats
Programs can have four styles of implementation comments: block, single-line, trailing and
end-of-line.
5.1.1 Block Comments
Block comments are used to provide descriptions of files, methods, data structures and
algorithms. Block comments may be used at the beginning of each file and before each
method. They can also be used in other places, such as within methods. Block comments inside
a function or method should be indented to the same level as the code they describe.
A block comment should be preceded by a blank line to set it apart from the rest of the code.
/*
* Here is a block comment.
*/
Block comments can start with /*-, which is recognized by indent(1) as the beginning of a
block comment that should not be reformatted. Example:
/*-
* Here is a block comment with some very special
* formatting that I want indent(1) to ignore.
*
* one
* two
* three
*/
Note: If you don’t use indent(1), you don’t have to use /*- in your code or make any other
concessions to the possibility that someone else might run indent(1) on your code.
See also “Documentation Comments” on page 8.
5.1.2 Single-Line Comments
Short comments can appear on a single line indented to the level of the code that follows. If a
comment can’t be written in a single line, it should follow the block comment format (see
section 5.1.1). A single-line comment should be preceded by a blank line. Here’s an example
of a single-line comment in Java code:
if (condition) {
/* Handle the condition. */
...
}
5.1.3 Trailing Comments
Very short comments can appear on the same line as the code they describe, but should be
shifted far enough to separate them from the statements. If more than one short comment
appears in a chunk of code, they should all be indented to the same tab setting.
Here’s an example of a trailing comment in Java code:
if (a == 2) {
return TRUE; /* special case */
} else {
return isPrime(a); /* works only for odd a */
}
5.1.4 End-Of-Line Comments
The // comment delimiter can comment out a complete line or only a partial line. It shouldn’t
be used on consecutive multiple lines for text comments; however, it can be used in
consecutive multiple lines for commenting out sections of code. Examples of all three styles
follow:
if (foo > 1) {
// Do a double-flip.
...
}
else{
return false; // Explain why here.
}
//if (bar > 1) {
//
// // Do a triple-flip.
// ...
//}
//else{
// return false;
//}
5.2 Documentation Comments
Note: See “Java Source File Example” on page 18 for examples of the comment formats
described here.
For further details, see “How to Write Doc Comments for Javadoc” which includes
information on the doc comment tags (@return, @param, @see):
For further details about doc comments and javadoc, see the javadoc home page at:
Doc comments describe Java classes, interfaces, constructors, methods, and fields. Each doc
comment is set inside the comment delimiters /**...*/, with one comment per class,
interface, or member. This comment should appear just before the declaration:
/**
* The Example class provides ...
*/
public class Example { ...
Notice that top-level classes and interfaces are not indented, while their members are. The first
line of doc comment (/**) for classes and interfaces is not indented; subsequent doc comment
lines each have 1 space of indentation (to vertically align the asterisks). Members, including
constructors, have 4 spaces for the first doc comment line and 5 spaces thereafter.
If you need to give information about a class, interface, variable, or method that isn’t
appropriate for documentation, use an implementation block comment (see section 5.1.1) or
single-line (see section 5.1.2) comment immediately after the declaration. For example, details
about the implementation of a class should go in in such an implementation block comment
following the class statement, not in the class doc comment.
Doc comments should not be positioned inside a method or constructor definition block,
because Java associates documentation comments with the first declaration after the comment.
6 – Declarations
6.1 Number Per Line
One declaration per line is recommended since it encourages commenting. In other words,
int level; // indentation level
int size; // size of table
is preferred over
int level, size;
Do not put different types on the same line. Example:
int foo, fooarray[]; //WRONG!
Note: The examples above use one space between the type and the identifier. Another
acceptable alternative is to use tabs, e.g.:
int level; // indentation level
int size; // size of table
Object currentEntry; // currently selected table entry
6.2 Initialization
Try to initialize local variables where they’re declared. The only reason not to initialize a
variable where it’s declared is if the initial value depends on some computation occurring first.
6.3 Placement
Put declarations only at the beginning of blocks. (A block is any code surrounded by curly
braces “{” and “}”.) Don’t wait to declare variables until their first use; it can confuse the
unwary programmer and hamper code portability within the scope.
void myMethod() {
int int1 = 0; // beginning of method block
if (condition) {
int int2 = 0; // beginning of "if" block
...
}
}
The one exception to the rule is indexes of for loops, which in Java can be declared in the for
statement:
for (int i = 0; i < maxLoops; i++) { ... }
Avoid local declarations that hide declarations at higher levels. For example, do not declare the
same variable name in an inner block:
int count;
...
myMethod() {
if (condition) {
int count; // AVOID!
...
}
...
}
6.4 Class and Interface Declarations
When coding Java classes and interfaces, the following formatting rules should be followed:
• No space between a method name and the parenthesis “(“ starting its parameter list
• Open brace “{” appears at the end of the same line as the declaration statement
• Closing brace “}” starts a line by itself indented to match its corresponding opening
statement, except when it is a null statement the “}” should appear immediately after the
“{“
class Sample extends Object {
int ivar1;
int ivar2;
Sample(int i, int j) {
ivar1 = i;
ivar2 = j;
}
int emptyMethod() {}
...
}
• Methods are separated by a blank line
7 - Statements
7.1 Simple Statements
Each line should contain at most one statement. Example:
argv++; // Correct
argc++; // Correct
argv++; argc--; // AVOID!
7.2 Compound Statements
Compound statements are statements that contain lists of statements enclosed in braces
“{ statements }”. See the following sections for examples.
The enclosed statements should be indented one more level than the compound statement.
The opening brace should be at the end of the line that begins the compound statement; the closing brace should begin a line and be indented to the beginning of the compound tatement.
Braces are used around all statements, even single statements, when they are part of a control structure, such as a if-else or for statement. This makes it easier to add statements without accidentally introducing bugs due to forgetting to add braces.
7.3 return Statements
A return statement with a value should not use parentheses unless they make the return value
more obvious in some way. Example:
return;
return myDisk.size();
return (size ? size : defaultSize);
7.4 if, if-else, if else-if else Statements
The if-else class of statements should have the following form:
if ( condition) {
statements;
}
if ( condition) {
statements;
} else {
statements;
}
if ( condition) {
statements;
} else if ( condition) {
statements;
} else {
statements;
}
Note: if statements always use braces {}. Avoid the following error-prone form:
if ( condition) //AVOID! THIS OMITS THE BRACES {}!
statement;
7.5 for Statements
A for statement should have the following form:
for ( initialization; condition; update) {
statements;
}
An empty for statement (one in which all the work is done in the initialization, condition, and
update clauses) should have the following form:
for ( initialization; condition; update);
When using the comma operator in the initialization or update clause of a for statement, avoid
the complexity of using more than three variables. If needed, use separate statements before
the for loop (for the initialization clause) or at the end of the loop (for the update clause).
7.6 while Statements
A while statement should have the following form:
while ( condition) {
statements;
}
An empty while statement should have the following form:
while ( condition);
7.7 do-while Statements
A do-while statement should have the following form:
do {
statements;
} while ( condition);
7.8 switch Statements
A switch statement should have the following form:
switch ( condition) {
case ABC:
statements;
/* falls through */
case DEF:
statements;
break;
case XYZ:
statements;
break;
default:
statements;
break;
}
Every time a case falls through (doesn’t include a break statement), add a comment where the
break statement would normally be. This is shown in the preceding code example with the
/* falls through */ comment.
Every switch statement should include a default case. The break in the default case is
redundant, but it prevents a fall-through error if later another case is added.
7.9 try-catch Statements
A try-catch statement should have the following format:
try {
statements;
} catch (ExceptionClass e) {
statements;
}
A try-catch statement may also be followed by finally,
which executes regardless of whether or not the try block has completed successfully.
try {
statements;
} catch (ExceptionClass e) {
statements;
} finally {
statements;
}
8 - White Space
8.1 Blank Lines
Blank lines improve readability by setting off sections of code that are logically related.
Two blank lines should always be used in the following circumstances:
Between sections of a source file
Between class and interface definitions
One blank line should always be used in the following circumstances:
Between methods
Between the local variables in a method and its first statement
Before a block (see section 5.1.1) or single-line (see section 5.1.2) comment
Between logical sections inside a method to improve readability
8.2 Blank Spaces
Blank spaces should be used in the following circumstances:
A keyword followed by a parenthesis should be separated by a space. Example:
while (true) {
...
}
Note that a blank space should not be used between a method name and its opening parenthesis. This helps to distinguish keywords from method calls.
A blank space should appear after commas in argument lists.
All binary operators except . should be separated from their operands by spaces. Blank spaces should never separate unary operators such as unary minus, increment (“++”), and decrement (“--”) from their operands. Example:
a += c + d;
a = (a + b) / (c * d);
while (d++ = s++) {
n++;
}
prints("size is " + foo + "\n");
The expressions in a for statement should be separated by blank spaces. Example:
for (expr1; expr2; expr3)
Casts should be followed by a blank space. Examples:
myMethod((byte) aNum, (Object) x);
myMethod((int) (cp + 5), ((int) (i + 3))
+ 1);
9 - Naming Conventions
Naming conventions make programs more understandable by making them easier to read.
They can also give information about the function of the identifier—for example, whether it’s a
constant, package, or class—which can be helpful in understanding the code.
|Identifier Type |Rules for Naming |Examples |
|Packages |The prefix of a unique package name is |com.sun.eng |
|Classes |always written in all-lowercase ASCII letters |com.apple.quicktime.v2 |
|Interfaces |and should be one of the top-level domain |edu.cmu.cs.bovik.cheese |
|Methods |names, currently com, edu, gov, mil, net, org, |class Raster; |
|Identifier Type |Rules for Naming | |
|Constants |Except for variables, all instance, class, |int i; |
| |and class constants are in mixed case with a |char c; |
| |lowercase |float myWidth; |
| |first letter. Internal words start with | |
| |capital letters. Variable names should not | |
| |start with underscore _ or dollar sign $ | |
| |characters, even | |
| |though both are allowed. | |
| |Variable names should be short yet | |
| |meaningful. The choice of a variable name | |
| |should be mnemonic— that is, designed to | |
| |indicate to the casual observer the intent of| |
| |its use. One-character variable names should | |
| |be avoided except for temporary “throwaway” | |
| |variables. Common | |
| |names for temporary variables are i, j, k, m,| |
| |and n for integers; c, d, and e for | |
| |characters. | |
| |The names of variables declared class | |
| |constants | |
|Variables |The names of variables declared class |static final int MIN_WIDTH = 4; |
| |constants and of ANSI constants should be all|static final int MAX_WIDTH = 999; |
| |uppercase with words separated by underscores|static final int GET_THE_CPU = 1; |
| |(“_”). (ANSI constants should be | |
| |avoided, for ease of debugging.) | |
10 - Programming Practices
10.1 Providing Access to Instance and Class Variables
Don’t make any instance or class variable public without good reason. Often, instance
variables don’t need to be explicitly set or gotten—often that happens as a side effect of
method calls.
One example of appropriate public instance variables is the case where the class is essentially a
data structure, with no behavior. In other words, if you would have used a struct instead of a
class (if Java supported struct), then it’s appropriate to make the class’s instance variables
public.
10.2 Referring to Class Variables and Methods
Avoid using an object to access a class (static) variable or method. Use a class name instead.
For example:
classMethod(); //OK
AClass.classMethod(); //OK
anObject.classMethod(); //AVOID!
10.3 Constants
Numerical constants (literals) should not be coded directly, except for -1, 0, and 1, which can
appear in a for loop as counter values.
10.4 Variable Assignments
Avoid assigning several variables to the same value in a single statement. It is hard to read.
Example:
fooBar.fChar = barFoo.lchar = 'c'; // AVOID!
Do not use the assignment operator in a place where it can be easily confused with the equality
operator. Example:
if (c++ = d++) { // AVOID! (Java disallows)
...
}
should be written as
if ((c++ = d++) != 0) {
...
}
Do not use embedded assignments in an attempt to improve run-time performance. This is the
job of the compiler. Example:
d = (a = b + c) + r; // AVOID!
should be written as
a = b + c;
d = a + r;
10.5 Miscellaneous Practices
10.5.1 Parentheses
It is generally a good idea to use parentheses liberally in expressions involving mixed operators
to avoid operator precedence problems. Even if the operator precedence seems clear to you, it
might not be to others—you shouldn’t assume that other programmers know precedence as
well as you do.
if (a == b && c == d) // AVOID!
if ((a == b) && (c == d)) // USE
10.5.2 Returning Values
Try to make the structure of your program match the intent. Example:
if ( booleanExpression) {
return true;
} else {
return false;
}
should instead be written as
return booleanExpression;
Similarly,
if (condition) {
return x;
}
return y;
should be written as
return (condition ? x : y);
10.5.3 Expressions before ‘?’ in the Conditional Operator
If an expression containing a binary operator appears before the ? in the ternary ?: operator, it
should be parenthesized. Example:
(x >= 0) ? x : -x;
10.5.4 Special Comments
Use XXX in a comment to flag something that is bogus but works. Use FIXME to flag something
that is bogus and broken.
11 - Code Examples
11.1 Java Source File Example
The following example shows how to format a Java source file containing a single public class.
Interfaces are formatted similarly. For more information, see “Class and Interface
Declarations” on page 3 and “Documentation Comments” on page 8
19
/*
* @(#)Blah.java 1.82 99/03/18
*
* Copyright (c) 1994-1999 Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*/
package java.blah;
import java.blah.blahdy.BlahBlah;
/**
* Class description goes here.
*
* @version 1.82 18 Mar 1999
* @author Firstname Lastname
*/
public class Blah extends SomeClass {
/* A class implementation comment can go here. */
/** classVar1 documentation comment */
public static int classVar1;
/**
* classVar2 documentation comment that happens to be
* more than one line long
*/
private static Object classVar2;
/** instanceVar1 documentation comment */
public Object instanceVar1;
/** instanceVar2 documentation comment */
protected int instanceVar2;
/** instanceVar3 documentation comment */
private Object[] instanceVar3;
/**
* ... constructor Blah documentation comment...
*/
public Blah() {
// ...implementation goes here...
}
/**
* ... method doSomething documentation comment...
*/
public void doSomething() {
// ...implementation goes here...
}
/**
* ...method doSomethingElse documentation comment...
* @param someParam description
*/
public void doSomethingElse(Object someParam) {
// ...implementation goes here...
}
}
3 Round Trip Engineering
1 Beispiel Bank
[pic]
// Einfügen
2 Beispiel Wertpapier
public class A extends WP
{
public A()
{
}
}
public class D
{
public IWP theIWP;
public K theK;
public D()
{
}
}
public class FV extends WP
{
public K theK;
public FV()
{
}
}
public interface IWP
{
}
public class K
{
protected String n;
public D theD;
public FV theFV;
public K()
{
}
/**
@roseuid 3CCA79B6001F
*/
public String getN()
{
}
}
public class O extends WP
{
public O()
{
}
}
public abstract class WP implements IWP
{
public WP()
{
}
}
// Einfügen
3 Sinn und Zweck
Die Umsetzung der Design Klassen in Code kann partiell durch Codegenerierung erfolgen.
Im Rahmen der Codegenerierung werden z.B. Associations in eine Referenz auf die entsprechende Klasse umgesetzt. Der Name der Referenz entspricht der Rolle der Klasse. Die Anzahl der Pointer entspricht der Muliplizität der Association.
Die Implementierung der Operationen erfolgt durch Umsetzung der Interaction Diagrams, der Statechart Diagrams und des Pseudocodes aus dem Design Model. Dies geschieht in der Regel manuell.
Das Reeengineering dient dazu, im Code vorgenommene Änderungen wieder in die UML Diagramme zu integrieren. Somit befinden sich die Diagramme und der Code in einem einheitlichen Zustand, ohne die Änderungen am Code per Hand in die UML Notation übertragen zu müssen.
Die Kombination aus Codegenerierung und Reengineering bezeichnet man als Round Trip Engineering.
4 Tutorial für Round Trip Engineering mit Rational Rose
1 Grundeinstellungen
1 Setzen des CLASSPATH
Grundvoraussetzung ist ein korrekt installiertes Java SDK (hier das aktuelle 1.4) und somit eine komplett gesetzte CLASSPATH Variable.
Die CLASSPATH Variable wird z.B. vom Java Compiler verwendet, um die System-Bibliotheken (oder die eigenen) zu finden.
Unter Windows NT/2000 setzt man folgendermaßen:
Programme
Einstellungen
Systemsteuerung
System
Sytemeigenschaften
Erweitert
Umgebungsvariablen
Neu
Bei „Name der Variablen“: CLASSPATH eingeben
Bei „Wert der Variablen“ werden die Pfade zu den einzelnen JAR Dateien und notwendigen Verzeichnissen eingegeben. So sei das SDK unter E:\jdk1.4.0 installiert. Somit wird folgendes eingetragen:
E:\jk1.4.0\jre\lib\rt.jar;E:\jdk1.4.0\jre\lib\jce.jar;E:\jdk1.4.0\jre\lib\jsse.jar;E:\jdk1.4.0\jre\lib\jaws.jar;E:\jdk1.4.0\jre\lib\charsets.jar;E:\jdk1.4.0\jre\lib\sunrsaign.jar\;E:\jdk1.4.0\jre\lib\ext\dnsns.jar;E:\jdk1.4.0\jre\lib\ext\ldapsec.jar;E:\jdk1.4.0\jre\lib\ext\localedata.jar;E:\jdk1.4.0\lib\dt.jar;E:\jdk1.4.0\lib\htmlconverter.jar;E:\jdk1.4.0\lib\tools.jar;.
2 Anlegen eines Projektverzeichnisses
Z.B. auf Laufwerk F: das Verzeichnis JavaTest.
3 Starten von Rose
Die Einstellungen beziehen sich auf die Rational Rose 2000 enterprise Version. Somit können sich die Menüpunkte je nach Version unterscheiden, wobei die Vorgehensweise identisch ist.
Nach dem Starten wird beim Wizard „Create New Model“ das WorkModel „J2SE 1.3“ oder ein höher ausgewählt. Dies ist notwendig, damit Rose alle benötigten Java Klassen und Elemente auflösen kann.
4 Hinzufügen des Projektverzeichnisses zum CLASSPATH
Nun wird das Projektverzeichnis den Java Projekt Spezifikationen hinzugefügt:
Tools
Java
Project Specification
Icon: New
Directory
Unser Verzeichnis auf Laufwerk F auswählen und mit OK bestätigen
Mit OK das Verzeichnis übernehmen
5 Speichern des Rose Projektes
Nun wird das Rose Projekt gespeichert. Hier: in unserem Projektverzeichnis unter dem Namen HelloWorld.mdl.
2 Vorgehensweise am Beispiel HelloWorld
1 LogicalView: Erzeugen der Start Klasse
Das HelloWorld Projekt soll aus 2 Klassen bestehen. Die erste ist nur die Startklasse mit der Methode „main“, während die andere Klasse „HelloWorldGUI“ das eigentliche Programm bildet. In dieser Klasse soll eine JFrame erzeugt und die Meldung „Hello World“ ausgegeben werden.
Hieraus folgt folgendes UML Diagramm:
2 Erzeugen des Codes der Klasse
Markieren der Klasse „HelloWorld“
Tools
Java
Generate Java
Nun erscheint folgender Dialog
In diesem wird unsere Klasse einem Verzeichnis (welches vorher in der Classpath Variable aufgenommen wurde) zugewiesen. Dies muss für jede Klasse nur 1 mal absolviert werden.
Select Java Files oder SelectAll
Select Class Path Entry
Nach dem Bestätigen des „Assign“ Buttons und des OK Buttons wird der Code erzeugt.
3 Bearbeiten des Codes
Die Klasse HelloWorld.java wird nun in einem Editor (oder IDE) geladen und sieht folgendermaßen aus:
//Source file: F:\\JAVATEST\\HelloWorld.java
public class HelloWorld
{
}
Nun wir den restlichen Code (hier blau):
//Source file: F:\\JAVATEST\\HelloWorld.java
public class HelloWorld
{
public static void main(String args[])
{
HelloWorldGUI hwg = new HelloWorldGUI();
Hwg.setSize(300,100);
hwg.setLocation(100,100);
hwg.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
System.exit(0);
}
});
hwg.setVisible(true);
}
}
Somit ergibt sich die nächste Klasse aus dem Code: HelloWorldGUI. Diese wird nun ebenfalls erst in UML verfasst und dann ebenfalls mittels Codeerzeugung generiert.
Vorher ist es jedoch sinnvoll das UML Diagramm der HelloWorld Klasse auf den neuesten (den die Datei HelloWorld.java vorgibt) Stand zu bringen.
4 ReverseEngineering der HelloWorld Klasse
Tools
Java
Reverse Engineer Java:
Auswahl des Verzeichnisses im Verzeichnisbaum
Mittels „Add“ oder „AddAll“ bei mehreren Dateien die Klasse HelloWorld.java aufnehmen
Per „SelectAll“ oder per Click auf den Namen die upzudatende Klasse auswählen.
Per „Reverse“ starten
Wenn keine Fehler gemeldet wurden einfach Fenster per „Done“ schließen
Ergebnis:
Die Datei HelloWorld.java darf nicht gelöscht werden, da sonst deren Zustand nicht komplett per Codeerzeugung hergestellt werden kann. D.h. Das UML Diagramm und die Datei bilden jetzt eine Einheit und sind nicht trennbar.
5 Erzeugen von HelloWorldGUI
Das UML Diagramm wird erweitert :
[pic]
6 Erzeugen des Codes der Klasse
Der Code wird wie unter 2.3.4.3.3.4.2.2 beschreiben erzeugt
7 Bearbeiten des Codes
Der Code sieht folgendermaßen aus
//Source file: F:\\JAVATEST\\HelloWorldGUI.java
import javax.swing.*;
import java.awt.*;
public class HelloWorldGUI extends JFrame
{
public HelloWorldGUI()
{
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
}
public void paint(Graphics g)
{
g.drawString(„HelloWorld“,100,50);
}
}
8 ReverseEngineering der HelloWorldGUI Klasse
Durchführung wie oben beschrieben
4 Integrieren des Systems
Der Integrator integriert das System in Übereinstimmung mit dem Build Plan.
5 Test
1 Beispiel Klassentest MyMath
Kleine Testumgebung für den Klassentest einer selbst geschriebenen MyMath Klasse.
1 BlackBoxTest
1 Testling Interface
public interface IMiniMath
{
public double calcDurch (double a, double b);
public double calcPotenz (double b, int n);
public double calcMal (double a, double b);
public double calcExp (double x, double epsilon);
public int getError();
public void resetError();
}
2 Testapplikation
public class TestApp
extends JFrame
implements ActionListener
{
private TestController testController = null;
private Tracer tracer = null;
public TestApp()
{
super("Test Applikation");
addWindowListener(new WindowClosingAdapter());
JMenuBar menubar = new JMenuBar();
menubar.add(createFileMenu());
setJMenuBar(menubar);
testController = new TestController();
tracer = new Tracer();
}
public void actionPerformed(ActionEvent event)
{
String c = event.getActionCommand();
System.out.println(c);
if (c.equals("Start"))
{
System.out.println("Start Test");
testController.start();
}
if (c.equals("Stop"))
{
System.out.println("Stop Test");
testController.stop();
}
if (c.equals("Beenden"))
{
System.out.println("Beenden");
tracer.shutDown();
}
}
//---Private Methoden---------------
private JMenu createFileMenu()
{
JMenu ret = new JMenu("Test");
ret.setMnemonic('T');
JMenuItem mi;
//Starten
mi = new JMenuItem("Start", 'S');
setCtrlAccelerator(mi, 'S');
mi.addActionListener(this);
ret.add(mi);
//Separator
ret.addSeparator();
//Speichern
mi = new JMenuItem("Stop", 'p');
setCtrlAccelerator(mi, 'S');
mi.addActionListener(this);
ret.add(mi);
//Separator
ret.addSeparator();
//Beenden
mi = new JMenuItem("Beenden", 'B');
setCtrlAccelerator(mi, 'B');
mi.addActionListener(this);
ret.add(mi);
return ret;
}
private void setCtrlAccelerator(JMenuItem mi, char acc)
{
KeyStroke ks = KeyStroke.getKeyStroke(
acc, Event.CTRL_MASK
);
mi.setAccelerator(ks);
}
public static void main(String[] args)
{
TestApp frame = new TestApp();
frame.setLocation(100, 100);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
3 Tracer
public class Tracer {
static Tracer tracer = null;
private Calendar calendar = null;
private PrintWriter out = null;
public Tracer ()
{
tracer = this;
calendar = Calendar.getInstance( TimeZone.getTimeZone( "ECT" ));
try
{
out = new PrintWriter(
new FileWriter( "TracerLog.txt" ));
}
catch (IOException ioe)
{
}
}
public void shutDown()
{
out.close();
String s = getTime() + " Monitor File closed !";
System.out.println(s);
out.println(s);
}
static public void put(String s)
{
tracer.send(s);
}
public void send(String s)
{
String str = getTime() + " Traceeintrag : " + s;
System.out.println(str);
out.println(str);
}
private String getTime() {
calendar.setTime( new Date());
return setLeadingZero( calendar.get( Calendar.HOUR_OF_DAY ))
+ ":" + setLeadingZero( calendar.get( Calendar.MINUTE ))
+ ":" + setLeadingZero( calendar.get( Calendar.SECOND ));
}
private String setLeadingZero( int i ) {
return ( i < 10 ) ? "0" + String.valueOf( i ): String.valueOf( i );
}
}
4 TestController
public class TestController {
static public boolean whiteBoxTest = false;
public TestController ()
{
}
public void start()
{
Tracer.put("Start des Testlaufs !!!");
// Test der Klasse MiniMath
IMiniMath mm = new MiniMath();
double result;
/*
// BlackBoxTest
// Testserie für die Funktion calcDurch()
// Testfall Standarddivision
mm.resetError();
result = mm.calcDurch(3.5, 0.5);
Tracer.put("Result " + result);
Tracer.put("error " + mm.error);
Tracer.put(" Testfall Ende");
// Testfall Division durch negative Zahl
mm.resetError();
result = mm.calcDurch(0.8, -0.2);
Tracer.put("Result " + result);
Tracer.put("error " + mm.error);
Tracer.put(" Testfall Ende");
// Testfall Division durch Null
mm.resetError();
result = mm.calcDurch(3, 0);
Tracer.put("Result " + result);
Tracer.put("error " + mm.error);
Tracer.put(" Testfall Ende");
*/
/*
// BlackBoxTest
// Testserie für die Funktion calcPotenz()
// Testfall Standard
mm.resetError();
result = mm.calcPotenz(2, 3);
Tracer.put("Result " + result);
Tracer.put("error " + mm.error);
Tracer.put(" Testfall Ende");
// Testfall Negativer Exponent
mm.resetError();
result = mm.calcPotenz(2, -3);
Tracer.put("Result " + result);
Tracer.put("error " + mm.error);
Tracer.put(" Testfall Ende");
// Testfall Exponent 0
mm.resetError();
result = mm.calcPotenz(2, 0);
Tracer.put("Result " + result);
Tracer.put("error " + mm.error);
Tracer.put(" Testfall Ende");
*/
// WhiteBoxTest
whiteBoxTest = true;
// Testserie für die Funktion calcPotenz()
// Testfall Standard
mm.resetError();
result = mm.calcPotenz(2, 3);
Tracer.put("Result " + result);
Tracer.put("error " + mm.getError());
Tracer.put(" Testfall Ende");
// Testfall Negativer Exponent
mm.resetError();
result = mm.calcPotenz(2, -3);
Tracer.put("Result " + result);
Tracer.put("error " + mm.getError());
Tracer.put(" Testfall Ende");
// Testfall Exponent 0
mm.resetError();
result = mm.calcPotenz(2, 0);
Tracer.put("Result " + result);
Tracer.put("error " + mm.getError());
Tracer.put(" Testfall Ende");
// End of WhiteBoxTest
whiteBoxTest = false;
/*
// BlackBoxTest
// Testserie für die Funktion calcExp()
// Testfall exp(1)
result = mm.calcExp(1, 0.001);
Tracer.put("Result " + result);
Tracer.put(" Testfall Ende");
*/
}
public void stop()
{
Tracer.put("Beenden des Testlaufs !!!");
}
}
5 Testprozedur
Doppelclick auf TestApp im Verzeichnis .../TestSystem
Aktivieren des Codes für den Testfall durch Entfernen der Kommentare
Ausführen der TestApp Applikation
Click auf MenueItem Start
Öffnen der Datei ExpectedTracerLog.txt im Verzeichnis .../TestSystem
Vergleich der erwarteten Ausgaben mit den Ausgaben der Datei ExpectedTracerLog.txt im Verzeichnis .../TestSystem
6 Einige Testfälle
1 Test calcDurch()
1 Testfall Standarddivision
1 Voraussetzungen
error = 0
2 Eingabe
a = 3.5
b = 0.5
3 Erwartete Ausgabe
7
error = 0
4 Ausgabe
7
error = 0
2 Testfall Division durch negative Zahl
1 Voraussetzungen
error = 0
2 Eingabe
a = 0.8
b = -0.2
3 Erwartete Ausgabe
-4
error = 0
4 Ausgabe
-4
error = 0
3 Testfall Division durch Null
1 Voraussetzungen
error = 0
2 Eingabe
a = 3
b = 0
3 Erwartete Ausgabe
0
error = -1
4 Ausgabe
Infinity
error = 0
2 Test calcPotenz()
1 Testfall Standard
1 Voraussetzungen
error = 0
2 Eingabe
b = 2
n = 3
3 Erwartete Ausgabe
8
error = 0
4 Ausgabe
8
error = 0
2 Testfall Negativer Exponent
1 Voraussetzungen
error = 0
2 Eingabe
b = 2
n = -3
3 Erwartete Ausgabe
0.125
error = 0
4 Ausgabe
0.5
error = 0
3 Testfall Exponent Null
1 Voraussetzungen
error = 0
2 Eingabe
b = 3
n = 0
3 Erwartete Ausgabe
1
error = 0
4 Ausgabe
2
error = 0
2 WhiteBoxTest
1 Testling
public class MiniMath implements IMiniMath
{
static int whileCounter_Block_2_3_while = 0;
private int error = 0;
public double calcDurch (double a, double b)
{
if (0 != a)
{
return a/b;
}
else
{
error = -1;
return 0;
}
}
public double calcPotenz (double b, int n)
{
int i;
double p = 0;
if (0 > b)
{
if (TestController.whiteBoxTest) Tracer.put("Block_1_if_then");
error = -1;
}
else
{
if (TestController.whiteBoxTest) Tracer.put("Block_2_if_else");
if (0 == n)
{
if (TestController.whiteBoxTest) Tracer.put("Block_2.1_if_then");
p = 1;
}
{
if (TestController.whiteBoxTest) Tracer.put("Block_2.2_if_else");
}
p = b;
i = 1;
while (i < n)
{
whileCounter_Block_2_3_while++;
if (TestController.whiteBoxTest) Tracer.put("Block_2.3_while Loops " + whileCounter_Block_2_3_while);
p = p * b;
i++;
}
if (0 > n)
{
if (TestController.whiteBoxTest) Tracer.put("Block_2.4_if_then");
p = 1/p;
}
else
{
if (TestController.whiteBoxTest) Tracer.put("Block_2.5_if_else");
}
}
return p;
}
public double calcMal (double a, double b)
{
return a*b;
}
public double calcExp (double x, double epsilon)
{
double summand = 1;
int k;
double expo;
if (x == 0)
{
expo = 0;
}
else
{
expo = 1;
k = 0;
do
{
summand = summand * x / (k + 1);
expo = expo + summand;
k = k+1;
}while (summand >= epsilon);
}
System.out.println("Math Funktion exp(x) = " + java.lang.Math.exp(x));
return expo;
}
public int getError()
{
return error;
}
public void resetError()
{
error = 0;
}
}
2 Test Log
TracerLog.txt
12:25:36 Traceeintrag : Start des Testlaufs !!!
12:25:36 Traceeintrag : Block_2_if_else
12:25:36 Traceeintrag : Block_2.2_if_else
12:25:36 Traceeintrag : Block_2.3_while Loops 1
12:25:36 Traceeintrag : Block_2.3_while Loops 2
12:25:36 Traceeintrag : Block_2.5_if_else
12:25:36 Traceeintrag : Result 8.0
12:25:36 Traceeintrag : error 0
12:25:36 Traceeintrag : Testfall Ende
12:25:36 Traceeintrag : Block_2_if_else
12:25:36 Traceeintrag : Block_2.2_if_else
12:25:36 Traceeintrag : Block_2.4_if_then
12:25:36 Traceeintrag : Result 0.5
12:25:36 Traceeintrag : error 0
12:25:36 Traceeintrag : Testfall Ende
12:25:36 Traceeintrag : Block_2_if_else
12:25:36 Traceeintrag : Block_2.1_if_then
12:25:36 Traceeintrag : Block_2.2_if_else
12:25:36 Traceeintrag : Block_2.5_if_else
12:25:36 Traceeintrag : Result 2.0
12:25:36 Traceeintrag : error 0
12:25:36 Traceeintrag : Testfall Ende
3 Kontrollflussgraph
2 Beispiel für Test Guidelines
Guidlines wurden für industrielle Bildverarbeitungs-Projekte mit einer Größe von ca. 5 Entwickler, 500 Klassen geschrieben.
1 Klassentest
1 Ziel
Der Klassentest überprüft das Zusammenspiel der einzelnen Operationen einer Klasse und das Zusammenspiel einer Klasse mit ihren Oberklassen.
Das Zusammenspiel mit Objekten anderer verwendeter Klassen wird dabei nicht explizit überprüft.
Verwendet die zu testende Klasse andere nicht implementierte Klassen, so werden diese durch Stubs ersetzt.
2 Zu testende Klassen
Alle wichtigen Klassen sind zu testen.
Existiert eine Vererbungshierarchie, so sind jeweils die letzten abgeleiteten Klassen zu testen. Ein isolierter Test von Schnittstellen oder abstrakten Basisklassen kann entfallen.
3 Black Box Test (Funktionaler Test)
1 Ziel
Der Specification Test oder Black Box Test verifiziert das externe Verhalten der Klasse.
Specification testing is done to verify the components behaviour without considering how the behaviour is implemented within the component. The specification tests thus look at what output the component will return, when given certain input and when starting in a particular state.
2 Definition der Testfälle
1 Allgemeine Hinweise
Beim Black Box Test werden die Testfälle aus der Spezifikation der Klasse abgeleitet.
Ein vollständiger Funktionstest ist i.A. nicht möglich.
Die Testfälle sind daher so auszuwählen, dass die Wahrscheinlichkeit groß ist, Fehler zu finden.
Alle wichtigen, kritischen und mit hohem Risiko behafteten Funktionen der Klasse sind zu testen.
Die funktionale Überdeckung soll mindestens 70% betragen.
Jede Unterklasse definiert einen neuen Kontext, der zu einem fehlerhaften Verhalten von geerbten Operationen führen kann.
Polymorphe Methodenaufrufe sind zu testen.
Überladene Operatoren und Methoden sind zu testen.
2 Äquivalenzklassenbildung
Zum Argumentbereich einer Operation gehören nicht nur ihre Eingangsparameter sondern auch der Zustand ihres Objektes.
Die Kombinationsmöglichkeiten für Input, Output und States ermöglichen in der Regel keine vollständige Testüberdeckung. Daher sind Input, Output und States in Äquivalenzklassen zu zerlegen und alle wichtigen Kombinationen von Äquivalenzklassen zu testen.
Es wird davon ausgegangen, dass ein Programm bei der Verarbeitung eines Repräsentanten aus einer Äquivalenzklasse so reagiert, wie bei allen anderen Werten dieser Äquivalenzklasse. Wenn das Programm mit dem repräsentativen Wert fehlerfrei läuft, dann ist zu erwarten, dass es auch für andere Werte aus dieser Äquivalenzklasse korrekt funktioniert.
Beispiel
1 System -> Umgebung -> Benutzervariablen -> PATH Variable bearbeiten: folgenden Eintrag hinzufügen: ;C:\CVS
3 Setzen des Classpath für den Taschenrechner
Damit nachfolgendes Beispiel auch richtig kompiliert und ausgeführt werden kann, muss CLASSPATH wie folgt gesetzt werden :
Für den Projektleiter werde Laufwerk E angenommen:
set CLASSPATH=%CLASSPATH%;E:\TaschenRechner;E:\TaschenRechner\gui;E:TaschenRechner\rechner
(Das Setzen kann temporär in der Kommandobox erledigt werden)
4 Anlegen des Projektverzeichnisses
Anlegen des Verzeichnisses auf Laufwerk E :
md E:\TaschenRechner
Wechseln in dieses Verzeichnis :
cd E:\TaschenRechner
5 Anlegen des Repository
CVS benötigt für seine Arbeit eine Art Datenbank, in der es die Projekte verwaltet. Diese Datenbank wird Repository genannt und stellt sich auf der Festplatte als ein Ordner mit Verwaltungs-, Konfigurationsdateien und Unterverzeichnissen dar.
Der Befehl hierzu lautet allgemein :
cvs –d :local:\ init
In unserem Beispiel :
Auf Laufwerk E wechseln :
md CVSRepos
Erstellen des Repository :
cvs –d:local:E:\CVSRepos\newRepos init
6 Setzen der CVSROOT Variablen
set CVSROOT=:local:E:\CVSRepos\newRepos
7 Sonderbehandlung von Binärdateien einstellen
Delta Speicherung
Beim allerersten Einchecken des Projektes speichert CVS eine Kopie jeder Datei. Werden nun Veränderungen an der Datei vorgenommen und diese eingecheckt, so wird nicht wieder eine Dateikopie angelegt, sondern die Änderung (zusammen mit internen Befehlen) in die bereits bestehende Datei geschrieben. Der Vorteil liegt in dem eingesparten Speicherplatz und in dem schnellen Zugriff von CVS auf Änderungen.
Ändern des Verhaltens bei Binärdateien
CVS ist nicht in der Lage, Binärdateien mittels Delta-Speicherung abzulegen. Da jedoch auch solche Dateien unter Kontrolle von CVS gestellt werden sollen, muss man CVS beim Einchecken von Binärdateien mitteilen, dass es sich um eine Binärdatei handelt und daher keine Delta Speicherung verwendet werden soll.
Damit nicht jedes mal beim Einchecken die Option „Binärdatei“ gesetzt werden muss, kann man Dateien mit bestimmten Namensendungen z.B. *.bin etc. in der CVS Steuerdatei cvswrappers als Binärdateien eintragen.
Um diese Steuerdatei zu editieren müssen die sogenannten Administrationsdateien ausgecheckt werden. Dazu erstellt man sich einen Tempordner, wechselt in diesen und ruft cvs folgendermaßen auf :
cvs –d checkout CVSROOT.
Im Beispiel :
Anlegen des Tempordners:
md E:\t
cd E: \t
CVSROOT auschecken:
cvs checkout CVSROOT
Ändern der Datei cvswrappers (Änderungen sind rot markiert)
# This file affects handling of files based on their names.
#
# The -t/-f options allow one to treat directories of files
# as a single file, or to transform a file in other ways on
# its way in and out of CVS.
#
# The -m option specifies whether CVS attempts to merge files.
#
# The -k option specifies keyword expansion (e.g. -kb for binary).
#
# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)
#
# wildcard [option value][option value]...
#
# where option is one of
# -f from cvs filter value: path to filter
# -t to cvs filter value: path to filter
# -m update methodology value: MERGE or COPY
# -k expansion mode value: b, o, kkv, &c
#
# and value is a single-quote delimited value.
# For example:
*.gif -k 'b'
*.class -k 'b'
*.jpeg -k 'b'
*.bin -k 'b'
*.exe -k 'b'
*.vep -k 'b'
Achtung :
Falls Word verwendet wird, Abspeicherung als Textfile ohne Extension .txt.
Die Einträge sind je nach Bedürfnis anzupassen.
Nach den Änderungen an den Administrationsdateien müssen diese wieder eingecheckt werden, damit CVS sich „umstellen“ kann. Der allgemeine Befehl zum Einchecken sieht wie folgt aus:
cvs commit –m .
Im Beispiel :
Einchecken :
cvs commit –m „Bin File Status geändert“
Löschen des Tempordners :
rd G:\t /S /Q. (/S = alle Dateien und Unterverzeichnisse; /Q = ohne Nachfrage)
3 Erstellen des Main Trunk durch den Projektleiter
1 Anlegen des Projektes
Das Anlegen des Projektes erfolgt mit dem Aufruf :
cvs import –m start
Durch den Aufruf wird das Projekt mit dem in das Repository „importiert“. Der Kommentar dient zur Charakterisierung des Eincheckvorganges und wird auch bei jedem weiteren Einchecken angeben (natürlich mit anderem Inhalt). Unter dem StartInitial ist eine Art Benutzerkennung zu verstehen. Sie kann z.B. aus dem Namenskürzel des Projektleiters bestehen.
Im Beispiel :
In das Verzeichnis TaschenRechner wechseln
Anlegen des Projektes :
cvs import -m "Start des Projektes mit dem Hauptverzeichnis" TaschenRechner cs start
Löschen des Verzeichnisses :
cd \ rd TaschenRechner /S /Q
Anschließend ist noch das soeben angelegt Projekt auszuchecken, da nicht direkt im Repository gearbeitet wird, sondern in einer Arbeitskopie :
Auschecken unseres Projektes :
cvs checkout TaschenRechner
2 Erzeugen des Package rechner
Das Package rechner wird durch Anlegen des gleichnamigen Verzeichnisses innerhalb des Projektverzeichnisses erreicht:
cd TaschenRechner
md rechner
Nun muss noch dieses unter die Kontrolle von CVS gestellt werden.
Neue Verzeichnisse innerhalb des Projektes werden mittels cvs add aufgenommen.
Im Beispiel :
cvs add rechner
Achtung :
Dateien in diesem neuen Verzeichnis werden nicht automatisch mit unter die Kontrolle von cvs gestellt !
Die Dateien müssen zusätzlich einzeln aufgenommen werden.
3 Erzeugen des Interfaces
Nun wird die Datei Rechner.java, die dass Interface darstellt in diesem Verzeichnis erzeugt :
// Rechner.java
package rechner;
public interface Rechner
{
public void setOperand(double operand);
public void setOperator(char operator);
public double getErgebnis();
}
4 Erzeugen einer Dummy Implementation des Interfaces
// RechnerImpl.java
package rechner;
public class RechnerImpl implements Rechner
{
public void setOperand(double operand)
{
}
public void setOperator(char operator)
{
}
public double getErgebnis()
{
return 7; //beliebiger DummyRückgabeWert
}
public RechnerImpl()
{
}
}
5 Aufnehmen der Dateien in das Projekt
cvs add Rechner.java
cvs add RechnerImpl.java
6 Einchecken der Dateien
cvs commit –m „Package rechner und Rechner.java und RechnerImpl.java aufgenommen“
Anmerkung :
Es werden alle Files eingecheckt.
Die Files können auch einzeln eingecheckt werden.
Verzeichnisse werden nicht mittels commit eingeckeckt sondern nur mittels add hinzugefügt.
7 Labeln der Konfiguration
Nun wird diesem Projektzustand ein symbolischer Name gegeben, um diesen speziellen Projektzustand wieder auschecken zu können.
cvs tag PackageRechnerUndInterface
8 Erzeugen das Package gui und der Datei TaschenRechnerGUI
Das Anlegen und Aufnehmen geschieht analog zu dem obigem Package rechner :
// TaschenRechnerGUI.java
package gui;
import rechner.Rechner;
import rechner.RechnerImpl;
public class TaschenRechnerGUI
{
private Rechner tr = null;
public TaschenRechnerGUI()
{
tr=new RechnerImpl();
}
}
Aufnehmen vom Projekthauptverzeichnis aus :
md gui
cvs add gui
cvs .\gui\add TaschenRechnerGUI.java
cvs commit –m „Package gui sowie Datei TaschenRechnerGUI aufgenommen“
Vergeben des symbolischen Namens PackageGUIundGUIklasse
cvs tag PackageGUIundGUIklasse
9 Erzeugen der Main Klasse TaschenRechner.java
Damit das Projekt auch gestartet werden kann, muss noch eine Startklasse mit einer main Funktion erstellt werden.
Die Datei Rechner.java wird im Projekthauptverzeichnis gespeichert :
//TaschenRechner.java
import gui.TaschenRechnerGUI;
public class TaschenRechner
{
public static void main(String args[])
{
TaschenRechnerGUI rechner=new TaschenRechnerGUI();
}
}
Nun wird auch diese Datei eingecheckt und dem Projekt der symbolische Name MainTrunkVorBranchBildung vergeben :
cvs add TaschenRechner.java
cvs commit –m „Startklasse Rechner.java erzeugt und aufgenommen“
cvs tag MainTrunkVorBranchBildung
10 Anlegen der Branches
Der Projektleiter ist nun mit dem Maintrunk fertig und teilt das Projekt den beiden Entwicklern zu. Damit beide unabhängig und ohne Störungen arbeiten können, werden von dem Maintrunk 2 Branches angelegt. Branch 1 für das GUI und Branch 2 für den eigentlichen Rechner. E1 ist für die GUI und E2 für den Rechner zuständig.
Auschecken der letzten lineup (Revision) :
cvs checkout TaschenRechner
In die Arbeitskopie wechseln :
cd TaschenRechner
Von dieser Arbeitskopie (die auf dem neusten Stand ist) ausgehend den neuen Branch anlegen:
cvs tag –b Entwickler-1-GUI
Mit diesem Befehl wird im Repository ein neuer Branch unter dem Namen Entickler-1-GUI angelegt. Die aktuell ausgecheckte Arbeitskopie ist noch nicht aus diesem neuen Branch. Deshalb kann nun im nächsten Schritt auch gleich der zweite Branch erzeugt werden.
Anlegen von Branch 2: rechner
cvs tag –b Entwickler-2-Rechner
11 Zugriff auf zentrales Repository ermöglichen
Entwickler 1 und 2 müssen Zugriff auf das zentrale Repository haben. Dies gelingt ggf. unter Verwendung von Netzlaufwerken.
4 Entwickeln der GUI durch Entwickler1
Der Entwickler 1 checkt nun seinen Zweig des Projektes aus und erhält dadurch eine Arbeitskopie aus seinem Branch.
Hiermit entwickelt er die GUI Klasse TaschenRechnerGUI.java und erstellt zum Testen auch eine kleine Testversion von TaschenRechnerImpl.java.
Nach der Fertigstellung und dem Test kann der Projektleiter die GUI Klasse wieder in den Maintrunk überführen.
1 Auschecken der Arbeitskopie vom Branch Entwickler-1-GUI
Allgemein :
cvs checkout –r
Im Beispiel :
cvs checkout –r Entwickler-1-GUI TaschenRechner
Die Arbeitskopie ist jetzt noch auf Stand der letzten MainTrunk Version des Projektes.
2 Codieren der GUI Klasse
In diesem einfachen Besipiel wird davon ausgegangen, dass der Entwickler die GUI Klasse in einem Stück schreibt und somit auch nur einmal eincheckt. In der Regel wird er seine Klasse jedoch in mehreren Schritten entwickeln und jede einzelne Version einchecken.
//Source file: P:\\TaschenRechner\\gui\\TaschenRechnerGUI.java
package gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import rechner.Rechner;
import rechner.RechnerImpl;
public class TaschenRechnerGUI extends JFrame implements ActionListener
{
private Rechner tr = null;
private JButton operatorPlus = new JButton (new ImageIcon ("/TaschenRechner/gui/OperatorPlus.gif"));
private JButton operatorMinus = new JButton (new ImageIcon ("/TaschenRechner/gui/OperatorMinus.gif"));
private JButton operatorMal = new JButton (new ImageIcon ("/TaschenRechner/gui/OperatorMal.gif"));
private JButton operatorDurch = new JButton (new ImageIcon ("/TaschenRechner/gui/OperatorDurch.gif"));
private JButton operatorGleich = new JButton (new ImageIcon ("/TaschenRechner/gui/OperatorGleich.gif"));
private JTextField ausgabeFenster = new JTextField (20);
private JButton operand0 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand0.gif"));
private JButton operand1 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand1.gif"));
private JButton operand2 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand2.gif"));
private JButton operand3 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand3.gif"));
private JButton operand4 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand4.gif"));
private JButton operand5 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand5.gif"));
private JButton operand6 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand6.gif"));
private JButton operand7 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand7.gif"));
private JButton operand8 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand8.gif"));
private JButton operand9 = new JButton (new ImageIcon ("/TaschenRechner/gui/Operand9.gif"));
private JButton operandP = new JButton (new ImageIcon ("/TaschenRechner/gui/OperandPunkt.gif"));
public TaschenRechnerGUI()
{
super("CVS - Testprojekt: Taschenrechner");
setSize(460,300);
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
tr=new RechnerImpl();
Container content=this.getContentPane();
content.setLayout(new FlowLayout());
operatorPlus.setActionCommand("plus");
operatorPlus.addActionListener(this);
operatorMinus.setActionCommand("minus");
operatorMinus.addActionListener(this);
operatorMal.setActionCommand("mal");
operatorMal.addActionListener(this);
operatorDurch.setActionCommand("durch");
operatorDurch.addActionListener(this);
operatorGleich.setActionCommand("gleich");
operatorGleich.addActionListener(this);
operand0.setActionCommand("0");
operand0.addActionListener(this);
operand1.setActionCommand("1");
operand1.addActionListener(this);
operand2.setActionCommand("2");
operand2.addActionListener(this);
operand3.setActionCommand("3");
operand3.addActionListener(this);
operand4.setActionCommand("4");
operand4.addActionListener(this);
operand5.setActionCommand("5");
operand5.addActionListener(this);
operand6.setActionCommand("6");
operand6.addActionListener(this);
operand7.setActionCommand("7");
operand7.addActionListener(this);
operand8.setActionCommand("8");
operand8.addActionListener(this);
operand9.setActionCommand("9");
operand9.addActionListener(this);
operandP.setActionCommand(".");
operandP.addActionListener(this);
content.add(operatorPlus);
content.add(operatorMinus);
content.add(operatorMal);
content.add(operatorDurch);
content.add(operatorGleich);
content.add(operand0);
content.add(operand1);
content.add(operand2);
content.add(operand3);
content.add(operand4);
content.add(operand5);
content.add(operand6);
content.add(operand7);
content.add(operand8);
content.add(operand9);
content.add(operandP);
content.add(ausgabeFenster);
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent we) {
System.exit(0);
}});
this.setVisible(true);
}
private void setOperand(char operand)
{
String szahl=ausgabeFenster.getText();
ausgabeFenster.setText(szahl+operand);
szahl=ausgabeFenster.getText();
tr.setOperand(Double.valueOf(szahl).doubleValue());
}
private void setOperator(char operator)
{
tr.setOperator(operator);
ausgabeFenster.setText("");
}
private void ausgabeOperator(char operator)
{
try {
tr.setOperator(operator); //da jetzt berechnet wird, kann Exception geworfen werden
}
catch (java.lang.ArithmeticException e) {
String message=e.getMessage();
if (message.equals("/ by zero"))
System.out.println("Division durch Null!");
}
double ergebnis=tr.getErgebnis();
ausgabeFenster.setText(String.valueOf(ergebnis));
}
private void punktOperand(char operator)
{
String szahl=ausgabeFenster.getText();
if (szahl.equals("")==true)
ausgabeFenster.setText("0.");
else
ausgabeFenster.setText(szahl+'.');
}
public void actionPerformed(ActionEvent e)
{
String command=e.getActionCommand();
char operand=command.charAt(0);
if (command.equals("plus"))
setOperator('+');
else
if (command.equals("minus"))
setOperator('-');
else
if (command.equals("mal"))
setOperator('*');
else
if (command.equals("durch"))
setOperator('/');
else
if (command.equals("gleich"))
ausgabeOperator('=');
else
switch(operand) {
case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case'8':case'9':
setOperand(operand);
break;
case '.':
punktOperand(operand);
}
}
}
Diese Datei unterscheidet sich völlig von der Version aus dem MainTrunk.
3 Einchecken der Arbeitskopie in den Branch Entwickler-1-GUI
cvs commit –m „GUI komplett fertiggestellt!“
cvs tag GetestetetVersion-Release1
5 Entwickeln des Rechners durch Entwickler2
1 Auschecken der Arbeitskopie vom Branch Entwickler-2-Rechner
cvs checkout –r Entwickler-2-Rechner TaschenRechner
2 Codieren der Rechnerlogik – Datei TaschenRechnerImpl.java
//Source file: P:\\Rechner\\rechner\\RechnerImpl.java
package rechner;
public class RechnerImpl implements Rechner
{
private double ergebnis;
private double operand1 = 0;
private double operand2 = 0;
private char operator;
private boolean operandOneEmpty;
private boolean operatorEmpty;
public void setOperand(double operand)
{
if (operandOneEmpty==true) { //noch keine Zahl eingegeben
operand1=operand;
}
else //jetzt die 2te Zahl abspeichern
operand2=operand;
}
public void setOperator(char operator)
{
if (operator=='=') {
operandOneEmpty=true;
berechne();
}
else {
this.operator=operator;
operandOneEmpty=false;
operatorEmpty=false;
}
}
public double getErgebnis()
{
if (operatorEmpty==false) { //nur wenn Rechenoperation gewählt wurde Rechne
operatorEmpty=true;
operatorEmpty=true;
operand1=ergebnis; //für den Fall, daß das Ergebnis aus einer vorherigen Rechnung direkt weiter verwendet wird und somit nach dem =OPerator ein anderer Operator folgt
return ergebnis;
}
else
return operand1;
}
public RechnerImpl()
{
operandOneEmpty=true;
operatorEmpty=true;
}
private void berechne() throws java.lang.ArithmeticException
{
switch (this.operator) {
case '+':
this.ergebnis=operand1+operand2; break;
case '-':
ergebnis=operand1-operand2; break;
case '*':
ergebnis=operand1*operand2; break;
case '/': {
if (operand2==0.0) {
ergebnis=0; //nicht ganz richtig (Divsision durch Null)
throw (new java.lang.ArithmeticException("/ by zero")) ;
}
ergebnis=operand1/operand2; //wenn oben keine Exception->alles OK-> berechne
}
}
}
}
3 Codieren der GUI Klasse als Testtreiber
Jetzt wird die TaschenRechnerGUI für einen kleinen Testlauf umgebaut (dies geschieht nur in diesem Branch und hat somit keinen Einfluss auf die gleichnamige Klasse von dem GUI-Branch).
// TaschenRechnerGUI.java -> nur zum Testen der RechnerLogik
package gui;
import rechner.Rechner;
import rechner.RechnerImpl;
public class TaschenRechnerGUI
{
private Rechner tr = null;
public TaschenRechnerGUI()
{
tr=new RechnerImpl();
rechne();
}
private void rechne() {
double erg=0;
//Addition
tr.setOperand(12); tr.setOperator('+'); tr.setOperand(2);
tr.setOperator('='); erg=tr.getErgebnis();
System.out.println("Addition: 12+2 = (Soll: 14) Ist: "+erg);
//Subtraktion
tr.setOperand(0.5); tr.setOperator('-'); tr.setOperand(12.5);
tr.setOperator('='); erg=tr.getErgebnis();
System.out.println ("Subtraktion: 0.5-12.5 = (Soll: -12) Ist: "+erg);
//Multiplikation
tr.setOperand(24.5); tr.setOperator('*'); tr.setOperand(2);
tr.setOperator('='); erg=tr.getErgebnis();
System.out.println ("Multiplikation: 24.5*2 = (Soll: 49) Ist: "+erg);
//Division
tr.setOperand(27.5); tr.setOperator('/'); tr.setOperand(5.5);
tr.setOperator('='); erg=tr.getErgebnis();
System.out.println ("Division: 27.5/5.5 = (Soll: 5) Ist: "+erg);
//Division durch Null
tr.setOperand(34); tr.setOperator('/'); tr.setOperand(0);
try {
tr.setOperator('=');
erg=tr.getErgebnis();
System.out.println ("Division: 34/0= "+erg);
}
catch (java.lang.ArithmeticException e) {
String message=e.getMessage();
if (message.equals("/ by zero"))
System.out.println ("Division durch Null! Division: 34/0= "+tr.getErgebnis());
else
System.out.println ("Division: 34/0= "+erg);
}
//Addition+Multiplikation
tr.setOperand(4.5); tr.setOperator('+'); tr.setOperand(5.5);
tr.setOperator('='); erg=tr.getErgebnis();
System.out.println ("Addition+Multiplikation: Teil 1: 4.5+5.5 = (Soll: 10) Ist: "+erg);
tr.setOperator('*'); tr.setOperand(2);
tr.setOperator('='); erg=tr.getErgebnis();
System.out.println ("Addition+Multiplikation: Teil 2: 10*2 = (Soll: 20) Ist: "+erg);
}
}
4 Einchecken der Arbeitskopie in den Branch Entwickler-2-Rechner
Zum Schluss noch einchecken und einen sym. Namen vergeben
cvs commit –m „Rechnerlogik gebaut und getestet“
cvs tag GetestetetRechnerVersion-Release1
6 Zusammenführen (Mergen) der Entwicklungsstränge durch den Projektleiter
Ggf. TaschenRechner Verzeichnis löschen :
rd TaschenRechner /s /q
Auschecken des aktuellen Zustandes des Maintrunks :
cvs checkout TaschenRechner
In das Arbeitsverzeichnis wechseln :
cd \TaschenRechner
Package gui in den Maintrunk mergen :
cvs update –j Entwickler-1-GUI gui
Package rechner in den Maintrunk mergen :
cvs update –j Entwickler-2-Rechner rechner
Einchecken der Änderungen :
cvs commit –m „Die Packages gui und rechner der beiden Branches mit dem Maintrunk zusammengeführt“
Vergabe des symbolischen Namens :
cvs tag Release-1
7 Darstellung der Entwicklungsstränge
CVS bietet die Möglichkeit, sich einen Überblick über den Entwicklungsstand des Projektes anhand der eingecheckten Dateien und deren Revisionen zu machen. Mit dem Befehl cvs log, ausgeführt im Projekthauptverzeichnis, wird die Logdatei auf dem Bildschirm ausgegeben. Damit eine Datei erstellt wird leitet man die Ausgabe einfach in eine Datei um. Mit cvs log > log.txt wird die Logdatei log.txt im Projekthauptverzeichnis erzeugt.
Die folgenden Beispielausgaben dokumentieren einen etwas komplexeren Entwicklungsprozess. Der Code wurde in mehreren Entwicklungsstufen erzeugt und eingecheckt. Dadurch ergeben sich für die einzelnen Dateien höhere Revisionsnummern.
RCS file: p:\CVSRepos\newRepos/TaschenRechner/Rechner.java,v
Working file: Rechner.java
head: 1.1
branch:
locks: strict
access list:
symbolic names:
MainTrunkVorBranchBildung: 1.1
keyword substitution: kv
total revisions: 1; selected revisions: 1
description:
----------------------------
revision 1.1
date: 2002/05/07 19:50:21; author: shikar; state: Exp;
Startklasse Rechner.java erzeugt und aufgenommen
=============================================================================
RCS file: p:\CVSRepos\newRepos/TaschenRechner/Taschenrechner.mdl,v
Working file: Taschenrechner.mdl
head: 1.5
branch:
locks: strict
access list:
symbolic names:
MainTrunkVorBranchBildung: 1.5
PackageGUIundGUIklasse: 1.4
TaschenRechnerImpl: 1.3
PackageRechnerUndInterface: 1.2
UseCaseInit: 1.1
keyword substitution: kv
total revisions: 5; selected revisions: 5
description:
----------------------------
revision 1.5
date: 2002/05/07 19:50:22; author: shikar; state: Exp; lines: +419 -257
Startklasse Rechner.java erzeugt und aufgenommen
----------------------------
revision 1.4
date: 2002/05/07 04:47:10; author: shikar; state: Exp; lines: +455 -788
Package gui sowie Datei und UML von TaschenRechnerGUI erzeugt
----------------------------
revision 1.3
date: 2002/05/07 04:27:27; author: shikar; state: Exp; lines: +270 -10
Datei TaschenRechnerImpl.java und UML erzeugt
----------------------------
revision 1.2
date: 2002/05/07 04:07:45; author: shikar; state: Exp; lines: +243 -3
Package rechner und TaschenRechner.java aufgenommen
----------------------------
revision 1.1
date: 2002/05/07 04:02:09; author: shikar; state: Exp;
UseCase Diagramme erzeugt und aufgenommen
=============================================================================
RCS file: p:\CVSRepos\newRepos/TaschenRechner/gui/TaschenRechnerGUI.java,v
Working file: gui/TaschenRechnerGUI.java
head: 1.2
branch:
locks: strict
access list:
symbolic names:
MainTrunkVorBranchBildung: 1.2
PackageGUIundGUIklasse: 1.1
keyword substitution: kv
total revisions: 2; selected revisions: 2
description:
----------------------------
revision 1.2
date: 2002/05/07 19:50:22; author: shikar; state: Exp; lines: +3 -3
Startklasse Rechner.java erzeugt und aufgenommen
----------------------------
revision 1.1
date: 2002/05/07 04:47:10; author: shikar; state: Exp;
Package gui sowie Datei und UML von TaschenRechnerGUI erzeugt
=============================================================================
RCS file: p:\CVSRepos\newRepos/TaschenRechner/rechner/TaschenRechner.java,v
Working file: rechner/TaschenRechner.java
head: 1.1
branch:
locks: strict
access list:
symbolic names:
MainTrunkVorBranchBildung: 1.1
PackageGUIundGUIklasse: 1.1
TaschenRechnerImpl: 1.1
PackageRechnerUndInterface: 1.1
keyword substitution: kv
total revisions: 1; selected revisions: 1
description:
----------------------------
revision 1.1
date: 2002/05/07 04:07:46; author: shikar; state: Exp;
Package rechner und TaschenRechner.java aufgenommen
=============================================================================
RCS file: p:\CVSRepos\newRepos/TaschenRechner/rechner/TaschenRechnerImpl.java,v
Working file: rechner/TaschenRechnerImpl.java
head: 1.2
branch:
locks: strict
access list:
symbolic names:
MainTrunkVorBranchBildung: 1.2
PackageGUIundGUIklasse: 1.2
TaschenRechnerImpl: 1.1
keyword substitution: kv
total revisions: 2; selected revisions: 2
description:
----------------------------
revision 1.2
date: 2002/05/07 04:47:10; author: shikar; state: Exp; lines: +7 -5
Package gui sowie Datei und UML von TaschenRechnerGUI erzeugt
----------------------------
revision 1.1
date: 2002/05/07 04:27:28; author: shikar; state: Exp;
Datei TaschenRechnerImpl.java und UML erzeugt
Was sagt diese Datei nun aus?
Es werden alle Dateien des Projektes mit den Erstellungs- und Änderungsinformationen, sowie den symbolischen Namen und der zugehörigen Revisionsnummer aufgelistet.
Im Einzelnen betrachtet:
Rechner.java:
• Aktuelle Revsion: 1.1
• Anzahl der gesamten Revisionen: 1
• Revision: 1.1 Erstellungsdatum: 2002/05/07 (eng. Darstellung ) um 19:50:21
o Zugehöriger Info Text: Startklasse Rechner.java erzeugt und aufgenommen
o Vorhanden in der Projektversion mit dem symbolischen Namen MainTrunkVorBranchBildung
TaschenRechnerGUI.java
• Aktuelle Revsion: 1.2
• Anzahl der gesamten Revisionen: 2
• Revision: 1.1 Erstellungsdatum: 2002/05/07 (eng. Darstellung ) um 04:47:10
o Zugehöriger Info Text: Package gui sowie Datei und UML von TaschenRechnerGUI erzeugt
o Vorhanden in der Projektversion mit dem symbolischen Namen PackageGUIundGUIklasse
• Revision: 1.2 Erstellungsdatum: 2002/05/07 (eng. Darstellung ) um 19:50:22 Zugehöriger Info Text: Startklasse Rechner.java erzeugt und aufgenommen
o Vorhanden in der Projektversion mit dem symbolischen Namen MainTrunkVorBranchBildung
TaschenRechner.java
• Aktuelle Revsion: 1.1
• Anzahl der gesamten Revisionen: 1
• Revision: 1.1 Erstellungsdatum: 2002/05/07 (eng. Darstellung ) um 04:07:46
o Zugehöriger Info Text: Package rechner und TaschenRechner.java aufgenommen Vorhanden in der Projektversion mit dem symbolischen Namen MainTrunkVorBranchBildung; PackageGUIundGUIklasse; TaschenRechnerImpl;; PackageRechnerUndInterface
TaschenRechnerImpl.java
• Aktuelle Revsion: 1.2
• Anzahl der gesamten Revisionen: 2
• Revision: 1.1 Erstellungsdatum: 2002/05/07 (eng. Darstellung ) um 04:27:28
o Zugehöriger Info Text: Datei TaschenRechnerImpl.java und UML erzeugt
o Vorhanden in der Projektversion mit dem symbolischen Namen TaschenRechnerImpl
• Revision: 1.2 Erstellungsdatum: 2002/05/07 (eng. Darstellung ) um 04:47:10
o Zugehöriger Info Text: Package gui sowie Datei und UML von TaschenRechnerGUI erzeugt
o Vorhanden in der Projektversion mit dem symbolischen Namen MainTrunkVorBranchBildung; PackageGUIundGUIklasse
3 Beispiel Configuration Management Unterstützung durch Rational Rose
1 Controlled Units
Per Default speichert Rose das gesamte Model in einem einzigen .mdl File.
Parallele Entwicklung wird durch die Zerlegung des Files in eine Menge von individuellen Files, sogenannte controlled units, ermöglicht.
controlled units sind die configuration elements für die Versionskontrolle.
Jedes Team ist für die Entwicklung und Wartung einer spezifischen unit verantwortlich.
Im Falle einer controlled unit enthält nicht mehr das .mdl file den Inhalt des Packages, sondern z.B. das .cat file. Das .mdl file enthält lediglich Referenzen auf die controlled units.
Falls die controlled units weitere controlled units enthalten, besitzen sie ebenfalls wiederum Referenzen auf die enthaltenen controlled units.
Die Struktur muss streng hierarchisch sein.
2 Verwalten von controlled units
Erzeugen :
Markieren des package
file
unit
control
Speichern des übergeordneten controlled units sichert die Referenzen auf die neu erzeugten controlled units.
Im Falle einer Versionskontrolle, muss die übergeordnete controlled unit vor dem Speichern ausgecheckt werden. D.h. für die übergeordnete controlled unit ensteht eine neue Version.
Laden :
file
units
load
Updaten :
file
units
reload
Importieren
file
import
Uncontrol
file
units
uncontrol
Beim Laden einer controlled unit aus einem read only file wird diese unit in rose als write-protected deklariert.
Eine checked-in unit ist daher automatisch write-protected im Gegensatz zu einer checked-out unit.
Das Importieren einer controlled unit fügt dem model lediglich eine Referenz auf das zugehörige file hinzu.
Uncontrol fügt den inhalt des zur unit gehörenden files in das file der umschließenden unit ein.
Das file der umschließenden unit muss zuvor ausgechecked werden.
4 Bereitstellen eines Project Repository
Z.B. mittels CVS
5 Erstellen von Base Lines
Z.B. mittels CVS
6 Erstellen von Change Requests
7 Deployment
1 Erstellen von Training Material
Overhead slides for classroom teaching.
Teacher's instructions.
Example programs, databases, and so on.
Textbooks, tutorials
2 Erstellen von User Support Material
User Guides
Maintenance Guides
Online demos
Online help system
Context-sensitive help
3 Erstellen von Release Notes
Release Notes identify changes and known bugs in a version of a build or deployment unit that has been made available for use.
4 Ausliefern an ß-Tester
It is a good idea for a company to keep a database of potential beta reviewers and an archive of their feedback.
Create a beta program to solicit feedback on the product under development from a subset of the intended users
8 Project Management
1 Erstellen einer Risk List
A sorted list of known and open risks to the project, sorted in decreasing order of importance and associated with specific mitigation or contingency actions.
2 Erstellen eines Iteration Plan
Z.B. mit MS Project
3 Aquire Staff
The Project Manager will have determined the staffing needs for the iteration and will look to the Human Resources function of the organization to provide staff with the needed domain, skills and experience profiles. Most organizations do not have the luxury of keeping a large pool of staff on stand-by for projects, and project starts do not always neatly synchronize with the termination of previous projects. Frequently then, except for a few staff engaged on the project from the outset, many will need to be hired. This may be a lengthy process, so the prudent Project Manager will always be looking ahead, and initiating the acquisition of staff for future iterations as well as the current one. It may be possible to cover shortfalls by working overtime or by the use of contract rather than permanent staff.
4 Erstellen von Work Orders
The work order is the Project Manager's means of communicating what is to be done, and when, to the responsible staff. It becomes an internal contract between the Project Manager and those assigned responsibility for completion.
9 Environment
1 Erstellen von Templates
Z.B. Template für Vision, Risk List, Glossary
2 Erstellen von Guidelines
Z.B. Programming Guidelines, Test Guidelines
3 Bereitstellen von Tools
Z.B. Eclipse, Rational Rose, CVS
4 Phasen des RUP
1 Zitate zum Begriff Komplexität
Autor unbekannt
Ein Arzt, ein Hochbauingenieur und ein Informatiker unterhielten sich darüber, was der älteste Beruf der Welt sei. Der Mediziner führte an, "Schon in der Bibel heißt es, dass Gott Eva aus Adams Rippe erschaffen hat. Dafür wurde natürlich die Medizin gebraucht, und so darf ich wohl behaupten, dass ich den ältesten Beruf der Welt habe."
Der Hochbauingenieur unterbrach ihn und sagte, "Aber noch vorher im Buch Genesis heißt es, dass Gott die Ordnung des Himmels und der Erde aus dem Chaos geschaffen hat. Das war der erste und wahrscheinlich der spektakulärste Einsatz des Bauingenieurwesens. Deshalb, lieber Doktor, irren Sie sich : Ich habe den ältesten Beruf der Welt."
Der Informatiker lehnte sich in seinem Stuhl zurück, lächelte und sagte dann verbindlich : "Und wer, meine Herren, glauben Sie, hat das Chaos erschaffen ? "
Booch
„Die Komplexität industriell einsetzbarer Software-Systeme überschreitet die Kapazität der menschlichen Intelligenz.“
2 Die Struktur komplexer Systeme
Nahezu alle komplexen Systeme nehmen dieselbe kanonische Form an :
Bild Kanonische Form komplexer Systeme
Komplexe Systeme sind hierarchisch. Die Ebenen der Hierarchie stellen auch unterschiedliche Abstraktionsebenen dar. Auf jeder Abstraktionsebene finden wir eine Ansammlung von Funktionseinheiten, welche zusammenarbeiten, um den höheren Ebenen bestimmte Dienste zur Verfügung zu stellen.
Im Prinzip müssen auf alle Teile eines komplexen Systems die Aktivitäten Requirements, Analyse, Design, Implementation und Test angewendet werden.
Wie kann man die Fülle von Aktivitäten sinnvoll strukturieren ?
3 Grundlegende Konzepte des RUP
1 Use Case Driven
Das Use Case Model beschreibt die komplette Funktionalität des Systems und ersetzt die traditionelle funktionale Spezifikation.
Das Analyse&Design Model wird ausgehend von den Use Cases erarbeitet und zeigt die Realisierung der Use Cases.
Die Implementation wird gegen die Use Cases getestet.
Use Cases bilden die Basis der Bedienungsanleitung.
Use Cases helfen die Reihenfolge der Releases festzulegen. Jede Release imlementiert eine Menge von Use Cases.
2 Architecture Centric
Use Cases und Architektur beeinflussen sich gegenseitig.
Die Architektur erwächst zunächst aus einigen Use Cases und ermöglicht dann die Realisierung weiterer oder zukünftiger Use Cases.
Damit werden die Use Cases dann auch durch die Architektur beeinflusst.
3 Iterative and Incremental
Sinnvollerweise unterteilt man große Softwareprojekte in viele kleine Miniprojekte. Jedes kleine Miniprojekt stellt eine Iteration dar und resultiert in einem Inkrement des Produktes.
Die inkrementelle Vorgehensweise besitzt einige Vorteile.
Kontrollierte Iterationen reduzieren das Kosten- und Zeitrisiko.
Fehler treten am Anfang der Entwicklung auf. Die Architektur kann daher meist noch ohne eine Verzögerung der Auslieferung korrigiet werden.
Die Iterationen erlauben eine Anpassung des Produktes an die Anforderungen des Anwenders. Die Anforderungen des Anwenders ändern sich in der Regel durch die Verwendung des Produktes.
Alle Beteiligten Personen lernen während des Entwicklungsprozesses. Dies erleichtert insbesondere die Einarbeitung neuer Entwickler.
Manche Risiken eines Projektes weden erst sichtbar, wenn erste Releases vorliegen.
Die iterative Vorgehensweise beinhaltet hingegen die Gefahr, echte Verbesserungen durch eine Serie nicht konvergenter Änderungen zu ersetzen. Das Softwaresystem wird instabil.
Solche Schleifen entstehen auch bereits in der Analyse-, der Entwurfs- oder der Implementationsphase.
Die verschiedenen Phasen Analyse, Entwurf sind daher nur schwer zu trennen.
4 Strukturierung des Entwicklungsprozesses
1 Phasen-Aktivitäten Diagramm
Man strukturiert einerseits die Lebenszeit eines Softwareproduktes in Phasen und andererseits die für das Produkt aufgewendete Arbeit in Aktivitäten. Die Aktivitäten werden in Disciplines gruppiert.
[pic]
Bild Phasen-Aktivitäten Diagramm
2 Disciplines
Business Modeling
Requirements
Analyse&Design
Implementation
Test
Deployment
Configuration&Change Management
Project Management
Environment
(vgl. oben)
3 Zyklen
Das Leben des Entwicklungsprozesses besteht aus einer Reihe von Zyklen. Jeder Zyklus endet mit der Auslieferung einer neuen Release des Systems an den Kunden.
Eine Release umfasst nicht nur den ausführbaren Code sondern alle für eine Wartung und Weiterentwicklung benötigten Model Elemente.
4 Phasen
Jeder Zyklus wird in die 4 Phasen Inception, Elaboration, Construction, Transition unterteilt.
Buch S. 11 Bild 1.5
Buch S. 104 Bild 5.6
Jede Phase kann von dem Manager oder dem Entwickler noch feiner in Iterationen unterteilt werden.
Im Prinzip kann jede Iteration als Miniwasserfall betrachtet werden, der alle Aktivitäten von Requirements bis Test durchläuft.
Je nach Phase ist jedoch das Ausmaß der einzelnen Aktivitäten unterschiedlich ausgeprägt. So liegt z.B. in der Inception Phase der Schwerpunkt auf der Ermittlung der Anforderungen, in der Elaboration Phase der Schwerpunkt auf Analyse&Design und in der Construction Phase liegt der Schwerpunkt auf Implementation und Test.
Jede Iteration führt zu einem Inkrement der Model Elemente, die das System repräsentieren.
5 Milestones
Jede Phase endet mit einem Meilenstein.
Ein Meilenstein bildet einen Synchronisationspunkt für parallel laufende Aktivitäten. Er ist definiert durch die Verfügbarkeit eine wohldefinierten Menge von Model Elementen und Dokumenten.
Jede kleine Iteration endet mit einem minor Milestone.
Diese kleinen Meilensteine dienen zur Reflexion des Zeitplans und des Budgets.
Meilensteine ermöglichen z.B., die Entscheidung zu treffen, ob die nächste Phase angegangen wird oder das Projekt abgebrochen wird.
Aufzeichnungen über den verwendeten Aufwand innerhalb eines Meilensteins erlauben bessere Schätzungen in zukünftigen Projekten.
4 Phasen des RUP und zugehörige primäre Aktivitäten
1 Inception
Das Ziel ist es, entscheiden zu können, ob eine Entwicklung des Produktes lohnt.
RUPTool
The scope of the project should be understood, and the stakeholders initiating the project should have a good understanding of the project's ROI (return on investment), i.e. what is returned, for what investment cost. Given this knowledge, a go/no go decision can be taken.
Die Vision des Endproduktes wird entwickelt.
Die Grenzen des Systems werden in Form von Actors identifiziert sowie die Interfaces zu externen Systemen auf einem abstrakten Level beschrieben.
Die Interaktion des Systems mit den Aktoren wird in Use Cases (< 10% der Use Case Masse) beschrieben.
Das Analysemodel (< 5% der gesamten Analyse) dient zur Strukturierung und präzisen Formulierung der Requirements.
Erste Überlegungen zum Kern der Architektur werden angestellt.
Der Focus liegt dabei auf neuen, riskanten oder schwierigen Teilen des Systems, die das gesamte Projekt gefährden können. Es geht an dieser Stelle nur darum abzuklären, ob eine Lösung existiert. Wir machen glaubhaft, dass eine Architektur existieren wird.
Erste Überlegungen zu Interfaces für Subsysteme, zur Middleware, zur Realisierung kritischer Use Cases, zu Supplementary Requirements, zum Deployment Model.
Critical risks werden identifiziert und in einer Risk List festgehalten.
Ein (Wegwerf-)Prototyp demonstriert den potentiellen Usern oder Käufern die Vorzüge des neuen Systems.
Prototypen dienen auch zur Untersuchung riskanter Systemteile.
(Nur bei dringendem Bedarf !)
Die erwarteten Aufwände werden sehr grob geschätzt (Zeit, Geld, Personal, Hardware usw.)
Der Markt und der Return of Investment wird grob geschätzt.
(Genaue Zahlenangaben sind zu diesem Zeitpunkt nicht möglich)
Die erwarteten Risiken werden identifiziert und priorisiert
Ggf. wir ein „Proof of concept“ Prototype erstellt.
Kleines Team bestehend aus
Project Manager
Architect
Experienced Developer
Test Engineer
User
Es kann nicht Alles dokumentiert werden.
!!! Die an der Inception Phase beteiligten Personen dienen daher auch als Gedächtnis des Teams !!!
2 Elaboration
Das Ziel ist es, die Architektur des Systems zu definieren.
RUPTool
The result of this initial iteration would be a first cut at the architecture, consisting of fairly described architectural views (use-case view, logical view, process view, deployment view, implementation view) and an executable architecture prototype.
Alle wichtigen Use Cases (80%) werden spezifiziert.
Eine Detailbeschreibung erfolgt für ca. 40% der Use Cases. Aber auch von diesen wird nur ein Bruchteil der Szenarios im Detail durchdacht.
Der Problembereich wird analysiert.
Das System wird in Packages zerlegt.
Die Realsierung der Use Cases wird überlegt.
Der Focus liegt auf den architekturrelevanten Use Cases (< 10%).
Nicht architekturrelevante Details werden in die Construction Phase verschoben.
Subsysteme, von denen man aus Erfahrung weiss, dass sie ohne Probleme designed werden können, werden nicht im Detail durchdacht.
Auf diese Weise entsteht ein Skelett des Systems.
Ggf. wird eine ausführbare erste Version des Systems zur Verifizierung der Architektur gebildet.
Die Architektur wird in mehreren Iterationen entwickelt in Abhängigkeut von der Größe des Systems, den Risiken, der Neuheit, der Komplexität und der Erfahrung der Entwickler.
Die Iterationen werden solange fortgesetzt, bis die Architektur stabil wird.
Es werden strategische Entscheidungen getroffen bzgl. Frameworks, Middleware (z.B. MFC, ODBC, Corba)
Die Risiken werden identifiziert und priorisiert.
Ggf. werden ausführbaren Prototypen zur Abschätzung von Risiken entwickelt.
Der Projektplan für den Bau des Systems in der Construction Phase wird entwickelt.
Der Projektplan identifiziert kontrollierte Serien von Releases.
Die Erstellung des Projektplans gelingt oft durch Anordnen der grundlegenden Szenarien. Die Anordnung wird z.B. so sortiert, dass die riskanten oder für den Kunden wichtigen Szenarien zuerst realisiert werden.
Es werden Testfälle entwickelt.
Es werden Ziele, Termine und Entwicklungsresourcen festgelegt.
Die Entwicklungsumgebung für die Construction Phase wird vorbereitet.
Die Architecture Baseline, die in der Elaboration Phase entwickelt wird, überlebt in Form einer Architecture Description.
Diese Beschreibung enthält den für das Verständnis der Architektur relevanten Teil der Model Elemente, Begründungen für strategische Entscheidungen, Performance- oder Speicheranforderungen, verwendete Patterns, kurz all das, was die Entwickler wissen müssen. Nicht enthalten sind z.B. Testfälle.
Kleines Team bestehend aus
Project Manager
Architect
Experienced Developer
Test Engineer
Wiederverwendungsmanager
Zukünftige Leiter der Subsysteme
User
3 Construction
Das Ziel ist die Erstellung einer „beta release“.
RUPTool
The main result of a late iteration in the construction phase is that more functionality is added, which yields an increasingly more complete system. The results of the current iteration are made visible to developers to form the basis of development for the subsequent iteration.
Das Produkt wird bis zur Einführung beim Kunden in einer Serie von Iterationen entwickelt. Jede Iteration implementiert eine Gruppe von Use Cases.
Component Engineers designen und implementieren die erforderlichen Subsysteme und Klassen.
Sie kompletieren die Requirements zu 100 %
Design user interface, Complete use case description
Sie vervollständigen die Use Case Realizations.
Sie designen Subsysteme und Klassen.
Sie implementieren Subsysteme und Klassen.
Service subsystems, die für einen use case benötigt werden, werden in der regel aus Effizienz Gründen vollständig implementiert und nicht nur soweit wie es den use case betrifft.
Die resultierenden Subsysteme und Klassen werden getestet. Zunächst werden sie einem Unit Test unterzogen und dann dem Integrationstest.
Der test engineer definiert test cases and test procedures zur Verifizierung der testbaren Requirements.
Er modifiziert test cases und test procedures der vorausgehenden builds für Regressionstests
Der System Integrator definiert die Folge der Builds in Form eines Integration Build Plan (meist bottom up).
Der System Integrator erzeugt nach dem Build Plan die Builds.
Der Integration Tester testet die Builds
Dazu führt er die Test Cases gemäß der Test Procedures aus
Für manche Klassen werden Stubbs benötigt um ein Build durchführen zu können.
Ferner werden driver implementiert um nicht vorhandene components zu simulieren
Gefundene Fehler werden gelogged und den Projekt Manager mitgeteilt
Die component engineers fixen die Fehler.
Die test engineer evaluieren die tests.
Ggf werden test cases modifiziert oder neue definiert.
Der Project manager vergleicht den aktuellen Stand des Projektes mit den Schätzungen aus der Elaboration Phase
Ein Maß für den Fortschritt des Projektes ist completion of builds and iterations im Vergleich zum Plan
Die gesammelten Erfahrungen werden in die Pläne für die zukünftigen Releases eingearbeitet
Viele Entwickler, großes Team.
Use Case engineer
Component engineer
Test engineer
System integrator
Itegration tester
System tester
System analyst
Architect
4 Transition
Das Ziel ist, die Ausliederung an den Kunden.
RUPTool
The transition phase culminates in the delivery to the customer of a complete system (and ancillary support artifacts) with functionality and performance as specified, and demonstrated in acceptance testing. The customer takes ownership of the software after a successful acceptance test.
Z.B. werden ß-Tester selektiert und Test Instruktionen vorbereitet.
Es wird ein User Manual oder Tutorial erstellt.
Das Produkt wird an die Anwender verteilt.
Die Anwender werden geschult.
Fehler werden korrigiert.
Schwere Fehler führen zu einer delta Release, kleinere werden auf die nächste Release verschoben.
Ggf. werden neue kleinere Anforderungen im Rahmen der vorhandenen Architektur realisiert.
Komponentenbasierte Softwareentwicklung am Beispiel von Enterprise Java Beans
1 Literatur
Enterprise Java Beans
Richard Monson-Haefel
O’Reilly 1999
2 Introduction
Wir betrachten eine E-Commerce Anwendungen im Internet.
Eine Bestellung mittels Internet tangiert in der Regel viele verteilte Glieder der Geschäftsprozesskette. So möchte der Anbieter z.B. vom Hersteller Angaben über individuelle Produktvarianten und Preise, vom Logistikpartner Auslieferungstermine und von seiner Bank ein Finanzierungsangebot. Zur Automatisierung solcher Geschäftsprozesse, hätte man gerne ein System von flexibel kombinierbaren und damit wiederverwendbaren Softwarekomponenten.
Diese Komponenten sollten ihre Daten darüberhinaus selbständig in eine Datenbank legen, Transaktionen beachten und auf verschiedenen Rechnern laufen können.
Enterprise JavaBeans (EJB) definiert ein standard distributed component model für die Entwicklung skalierbarer und portierbarer business systems.
Das Komponentenmodell erlaubt die Verteilung von business objects in Form von beans (Bohnen).
Ein EJB Server ist für object brokering, transaction management, security, persistence und concurrency verantwortlich.
Die Kapselung von Daten einer Datenbank in business object beans hat Vorteile gegenüber einem direkten Zugriff auf die Datenbank. Der Aufruf einer Methode eines objects ist in der Regel einfacher als die Ausführung eines SQL Statements. Die Objektifizierung von Daten mittels beans erleichtert die Wiederverwendung. Die Datenbankimplementierung kann modifiziert werden, ohne dass der Client etwas davon bemerkt.
Attribute der bean, z.B. Transaktionsverhalten, Persistenz und Sicherheit können über Properties ohne Code Modifikationen eingestellt werden.
Eine enterprise bean ist nicht nur betriebssystemunabhängig sondern auch unabhängig von der Implemenation des application servers sofern dieser die EJB Spezifiaktion implementiert.
1 Distributed Object Architectures
1 Three Tier Architecture
Distributed business objects bilden in der Regel die mittlere Schicht einer drei Schichten Architektur, bestehend aus GUI, business logic und einer Datenbank.
// Einfügen
Bild 3 Schichten Architektur
Distributed object technologie (Java EJB, CORBA, Microsoft DCOM) erlaubt die Verwendung der Objekte durch client applications auf einem anderen Computer.
Wie funktiononiert das ?
2 Stub und Skeleton
// Einfügen
Bild Stub Skeleton
Das Prinzip besteht darin, ein Stellvertreter-Objekt auf dem lokalen Computer so aussehen zu lassen wie das Orginal auf einem entfernten Computer.
Die Archietktur besteht aus dem objectServer, skeleton (Skelett) und stub (Stummel).
Jeder objectServer hat eine zugehörige stub- und skeleton-Klasse.
Stub und skeleton sind dafür verantwortlich, den objectServer so aussehen zu lassen, als würde er auf der lokalen client Maschine laufen.
Dies wird erreicht durch ein remote method invocation (RMI) Protokoll.
Java, Corba, Microsoft DCOM verwenden unterschiedliche RMI Protokolle.
// Einfügen
3 Interface und Server Technologie am vereinfachten Modell
Beispiel PersonModel
public interface Person {
public int getAge() throws Throwable;
public String getName() throws Throwable;
}
public class PersonServer implements Person {
int age;
String name;
public PersonServer(String name, int age){
this.age = age;
this.name = name;
}
public int getAge(){
System.out.println("getAge() called !");
return age++;
}
public String getName(){
System.out.println("getName() called !");
return name;
}
}
public class PersonClient {
public static void main(String [] args){
try{
Person person = new Person_Stub();
int age = person.getAge();
String name = person.getName();
System.out.println(name+" is "+age+" years old");
age = person.getAge();
name = person.getName();
System.out.println(name+" is "+age+" years old");
age = person.getAge();
name = person.getName();
System.out.println(name+" is "+age+" years old");
}catch(Throwable t) {t.printStackTrace();}
}
}
public class Person_Stub implements Person {
Socket socket;
public Person_Stub()throws Throwable{
}
public int getAge( ) throws Throwable{
// when this method is invoked, stream the method name to the
// skeleton
// create a network connection to the skeleton.
// Replace "myhost" with your own IP Address of your computer.
socket = new Socket("Edelmann",9000);
ObjectOutputStream outStream
= new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("age");
outStream.flush();
ObjectInputStream inStream = new
ObjectInputStream(socket.getInputStream());
return inStream.readInt();
}
public String getName()throws Throwable{
// when this method is invoked, stream the method name to the
// skeleton
// create a network connection to the skeleton.
// Replace "myhost" with your own IP Address of your computer.
socket = new Socket("Edelmann",9000);
ObjectOutputStream outStream
= new ObjectOutputStream(socket.getOutputStream());
outStream.writeObject("name");
outStream.flush();
ObjectInputStream inStream = new
ObjectInputStream(socket.getInputStream());
return (String)inStream.readObject();
}
}
public class Person_Skeleton extends Thread{
PersonServer myServer;
public Person_Skeleton(PersonServer server){
// get a reference to the object server that this skeleton wraps
this.myServer = server;
}
public void run(){
try{
// create a server socket on port 9000
ServerSocket serverSocket = new ServerSocket(9000);
// wait for and obtain a socket connection from stub
boolean finished = false;
while (!finished)
{
Socket socket = serverSocket.accept();
if(socket != null)
{
// create an input stream to receive requests from stub
ObjectInputStream inStream
= new ObjectInputStream(socket.getInputStream());
// Read next method request from stub. Block until request is
// sent.
String method = (String)inStream.readObject();
// Evalute the type of method requested
if(method.equals("age")){
// invoke business method on server object
int age = myServer.getAge();
// create an output stream to send return values back to
// stub.
ObjectOutputStream outStream
= new ObjectOutputStream(socket.getOutputStream());
// send results back to stub
outStream.writeInt(age);
outStream.flush();
}else if(method.equals("name")){
// invoke business method on server object
String name = myServer.getName();
// create an output stream to send return values back to
// the stub.
ObjectOutputStream outStream
= new ObjectOutputStream(socket.getOutputStream());
// send results back to stub
outStream.writeObject(name);
outStream.flush();
}
}
}
}catch(Throwable t) {t.printStackTrace();System.exit(0); }
}
public static void main(String args [] ){
// obtain a unique instance Person
PersonServer person = new PersonServer("Richard", 34);
Person_Skeleton skel = new Person_Skeleton(person);
skel.start();
}
}
Der stub implementiert ein interface mit denselben business-methods wie das serverObject. Jedoch enthalten die Methoden des stub keine business-logic. Stattdessen enthalten sie die Anweisungen, die erforderlich sind, um Anforderungen an den skeleton zu senden und die Ergebnisse zu empfangen.
Wenn ein Client eine Methode des stub aufruft, werden Name und Parameter als stream zum skeleton übertragen, dieser parst den stream und ruft die korrespondierende Methode des objectServers. Jeder Wert, den die gerufene Methode zurückliefert, wird als stream zum stub zurücktransportiert. Der stub liefert dann den Wert an den Client als ob die business logic lokal ausgeführt worden wäre.
Wirkliche distributed object protocols wie CORBA, DCOM, Java RMI generieren stub und skeleton und unterstützen exception handling. Sogenannte object request broker (ORB) erlauben die Verwaltung von serverObjects. Sie unterstützen transaction management ind security.
2 Server-Side Components (Selbststudium)
Die Einkapselung von business logic in business objects hat in jünster Zeit eine große Bedeutung in der IT Industrie erhalten. Wenn die Software, welche das business modelliert, in business objects gekapselt werden kann, wird sie flexibel, erweiterungsfähig und wiederverwendbar.
Ein server-side component model definiert eine Architektur für die Entwicklung verteilter business objects.
In diesem Sinne ist ein business System eine Ansammlung von server-side components wie customer, product, reservation, warehouse usw. Products können in dem warehouse gespeichert werden oder an den customer ausgeliefert werden. Ein customer kann ein product reservieren oder kaufen. Ein solches System ist flexibel, da es objektifiziert ist, und gut zugreifbar, weil es aus verteilten Objekten besteht.
Component Models
JavaBeans (package java.beans) ist ein Komponentenmodell für intraprocess Zwecke, EJB (package javax.ejb) ist ein Komponentenmodell für interprocess Zwecke.
JavaBeans kann etwa verwendet werden, um eine GUI zusammenzusetzen. Eine Komponente ist etwa ein PushButton. EJB wird hingegen verwendet, um eine verteilte business logic aufzubauen. Eine Komponente ist etwa ein customer object.
3 Component Transaction Monitors (Selbststudium)
Sogenannte application server managen die Komponenten zur Laufzeit und machen sie für clients verfügbar. Ein application server besteht in der Regel aus verschiedenen Technologien.
TP Transaction processing monitors
Transaction processing monitors sind operating systems für business systems. Sie managen Speicher, Datenbankzugriff und Transaktionen. Die business logic wird dabei durch prozedurale Applikationen gebildet. Der Zugriff erfolgt meist mittels remote procedure calls (RPC). TP monitors sind nicht objektorientiert.
ORB object request broker
Der ORB ist vergleichbar mit einer Telefonzentrale. Seine wesentliche Aufgabe besteht in der Übermittlung von Operationsaufrufen und ihren Ergebnissen zwischen Client- und Server-Objekten.
Der ORB benutzt dazu die Schnittstellen der Client und Server Objekte.
Die Übertragung basiert z.B. auf dem IIOP (Internet Inter ORB Protokoll). Dieses wiederum verwendet TCP/IP.
Clients erhalten Dienstleistungen über Anforderungen (Request).
Ein Request umfasst die Angabe des Servers, der Operation, der Ein- und Ausgabeparameter, optionaler Ausnahmebedingungen und optionaler Kontextangaben.
Für den Client ist nicht mehr erkennbar, welcher Prozess auf welchem Rechner seine Anforderung erfüllt.
Die Verlagerung von Diensten auf einen anderen Netzknoten oder eine Änderung der Netztopologie hat also keine Auswirkungen auf den Client.
Die Technologien für verteilte Sytseme wie etwa CORBA oder Java RMI sind aus der RPC Technologie erwachsen. Der signifikante Unterschied besteht darin, dass man eine Methode eines Objektes aufruft und nicht eine Funktion einer Applikation. Die verteilten Objekte werden gewöhnlich von einem ORB verwaltet.
ORB’s haben sich jedoch in Umgebungen mit einem hohen Volumen von Transaktionen als unbrauchbar erwiesen. Darüberhinaus überlassen sie die Behandlung von Transaktionen, Persistenz und concurrency der Applikation.
CTM component transaction monitor
Aus der traditionellen transaction Technologie und der ORB Technologie entwickelte sich (1999) der component transaction monitor (CTM) als hybrid model.
CTM`s managen concurrency, transactions, object distribution, load balancing, security, persistence und resource management. Entwickler müssen diese Möglichkeiten nicht implementieren.
CTM`s werden von der relational database Industrie, der application server Industrie, der web server Industrie und der CORBA ORB Industrie hergestellt.
Die Beziehung des CTM zu seinen components ist diesebe wie die Beziehung des Eisenbahnsystems zu seinen Zügen. Das Eisenbahnsystem kümmert sich um load balancing, concurrency und resourcen management. Das Eisenbahnsystem liefert die Infrastruktur für die Züge. Der Lokführer kümmert sich um den Transport und nicht um die Infrastruktur.
Verschiedene Eisenbahnsysteme haben etwa unterschiedliche Spurbreiten. Die Züge des einen Systems laufen nicht auf den Schienen des anderen Systems.
Der MTS ist Microsofts CTM
Der Microsoft Transaction Server (MTS) wurde 1996 eingeführt. Der Service für verteilte Objekte basiert auf der DCOM Technologie. Ein verwaltetes business object muss COM konform sein.
Die Verwendung von MTS erzwingt die Microsoft Plattform.
MTS unterstützt keine persistenten Komponenten.
CORBA Common Object Request Broker Architecture CTM`s
Die meisten nicht Microsoft CTM`s basieren auf dem offenen CORBA Standard als Technologie für verteilte Objekte.
Systemanbieter und Anwender objektorientierter Techniken haben sich 1989 zur OMG (Object Management Group) zusammengeschlossen, um Standards und Spezifikationen für eine Infrastruktur zu entwickeln, die für verteilte objektorientierte Anwendungen erforderlich ist. Diese Spezifikationen wurden zum de-facto-Standard in der Softwareindustrie.
(Z.Zt., d.h. 1999 ca 800 Firmen)
Die OMG arbeitet lediglich die Spezifikation aus. Sie überläßt die Erstellung der Software kommerziellen Anbietern.
Neben dem CORBA Standard benötigte man aber ein gemeinsames component model.
EJB Enterprise JavaBeans ist Sun’s CTM
Enterprise JavaBeans is a standard server-side component model for component transaction monitors.
19 97 gab sun die erste Spezifikation von Enterprise JavaBeans heraus.
Daher kann man nun erwarten, dass business objects, die dem EJB Standard enstprechen, in jedem CTM arbeiten, welches die EJB Spezifikation unterstützt.
IBM`s Sun Francisco business object framework wird sich dem EJB component model anpassen.
3 Architectural Overview
Man unterscheidet entity beans und session beans.
1 Beispiel CruiseBean
import javax.ejb.EntityContext;
import javax.ejb.FinderException;
public class CruiseBean implements javax.ejb.EntityBean
{
public String cruiseId;
public String cruiseName;
public String cruiseShip;
public String cruiseDate;
public int cruiseDays;
public int cruiseMaxPerson;
public String ejbCreate(String cruiseId, String cruiseName, String cruiseShip,String cruiseDate, int cruiseDays, int cruiseMaxPerson)
{
this.cruiseId = cruiseId;
this.cruiseName = cruiseName;
this.cruiseShip = cruiseShip;
this.cruiseDate=cruiseDate;
this.cruiseDays=cruiseDays;
this.cruiseMaxPerson=cruiseMaxPerson;
return null;
}
public String getCruiseId()
{
return cruiseId;
}
public String getName()
{
return cruiseName;
}
public void setName(String cruiseName)
{
this.cruiseName = cruiseName;
}
public void setShip(String cruiseShip)
{
this.cruiseShip = cruiseShip;
}
public String getShip()
{
return cruiseShip;
}
public void setDate(String cruiseDate)
{
this.cruiseDate = cruiseDate;
}
public String getDate()
{
return cruiseDate;
}
public int getMaxPerson()
{
return cruiseMaxPerson;
}
public void setMaxPerson(int cruiseMaxPerson)
{
this.cruiseMaxPerson = cruiseMaxPerson;
}
public int getDays()
{
return cruiseDays;
}
public void setDays(int cruiseDays)
{
this.cruiseDays = cruiseDays;
}
public void ejbPostCreate(String cruiseId, String cruiseName, String cruiseShip,String cruiseDate, int cruiseDays, int cruiseMaxPerson){}
public void setEntityContext(EntityContext ctx){}
public void unsetEntityContext(){}
public void ejbActivate(){}
public void ejbPassivate(){}
public void ejbLoad(){}
public void ejbStore(){}
public void ejbRemove(){}
}
EJB erfordert die Verwendung der Java RMI Konventionen.
2 Entity beans
// Einfügen
Mit anderen Worten modellieren entity beans real world objects. Diese objects entsprechen gewöhnlich persistenten records in einer Datenbank.
Im Beispiel eines Reisebüros benötigen wir entities welche etwa customer, cruise, cabins und reservations repräsentieren.
Entity beans modellieren Daten und Verhalten. Das Verhalten betrifft im Wesentlichen business logic, die sich direkt auf die Daten bezieht.
Entity beans repräsentieren business concepts, die als Nomen ausgedrückt werden können.
3 Beispiel TravelAgentBean
import javax.naming.InitialContext;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.DuplicateKeyException;
import javax.ejb.CreateException;
import javax.rmi.PortableRemoteObject;
import java.rmi.RemoteException;
import java.util.*;
import Reservation;
public class TravelAgentBean implements SessionBean
{
ReservationHome reservationHome; // Achtung : Kann fuer stateless session beans kritisch werden
Enumeration reservationListe; // Achtung : Kann fuer stateless session beans kritisch werden
public Reservation travelAgentReservation(String reservationId, String cruiseId, String customerId)
throws DuplicateKeyException, CreateException, DataExistsException
{
Reservation theReservation = null;
//String idReservation = reservationId;
try {
InitialContext context = new InitialContext();
Object objectReservationHomeRef = context.lookup("reservation");
reservationHome = (ReservationHome)PortableRemoteObject.narrow(objectReservationHomeRef, ReservationHome.class);
}
catch (Exception NamingException)
{
NamingException.printStackTrace();
}
// Pruefe, ob bereits Reservierung fuer den Kunden mit der vorgegebenen Kreuzfahrt existiert
boolean found = false;
try {
reservationListe = reservationHome.findAll();
while(reservationListe.hasMoreElements())
{
theReservation = (Reservation)reservationListe.nextElement();
if(cruiseId.equals(theReservation.getCruiseId()) && customerId.equals(theReservation.getCustomerId()))
{
found = true;
return null;
//throw new DataExistsException(); //noch nicht funktioniert
// break;
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
try{
if(found == false)
//theReservation = reservationHome.create(idReservation, cruiseId, customerId);
theReservation = reservationHome.create(reservationId, cruiseId, customerId);
}
catch (Exception e)
{
String message = e.getMessage();
e.printStackTrace();
}
return theReservation;
}
public Reservation getRecordReservation(String reservationId)//idReservation)
{
Reservation record = null;
try {
record = reservationHome.findByPrimaryKey(reservationId);//idReservation);
}
catch (java.rmi.RemoteException e)
{
String message = e.getMessage();
}
catch (javax.ejb.FinderException e)
{
e.printStackTrace();
}
return record;
}
public void ejbCreate() { }
public void setSessionContext(SessionContext context) { }
public void ejbRemove() { }
public void ejbActivate() { }
public void ejbPassivate() { }
public void ejbLoad() { }
public void ejbStore() { }
}
4 Session beans
// Einfügen
Session beans agieren als Agenten für den client.
Session beans managen die Interaktionen zwischen den entity beans.
Session beans sind der angemessene Platz für die business logic. Sie steuern unter Verwendung von entity beans den workflow. Sie repräsentieren daher den workflow. Der workflow beschreibt alle Schritte, die getan werden müssen, um eine bestimmte Arbeit zu erledigen.
Einige Aktionen kann man durch einen direkten Aufruf einer Methode der cruise bean erledigen. Jedoch weiß die cruise bean nichts über den context einer Aktion. Bucht man also etwa einen Passagier für ein Kreuzfahrtschiff, so muss man sicher eine cruise bean verwenden. Man muss jedoch eine Menge Wissen verwenden, welches nichts mit einem Schiff zu tun hat. Z.B. muss man etwas über den Passagier wissen, über den Ticketpreis, über den Fahrplan usw. Eine TravelAgent session bean wird eine cruise, eine customer und eine reservation bean verwenden, um eine Reservierung durchzuführen.
Die TravelAgent session bean repräsentiert in diesem Falle nicht die Menge der Kunden, Kreuzfahrten und Kabinen. Sie kann daher nicht als Entität aufgefasst werden. Sie repräsentiert lediglich das Wissen um Beziehungen, Vorschriften, Randbedingungen im Zusammenhang mit der Buchung. Sie repräsentiert das Wissen eines Travelagent aber nicht die Person.
Die komplexe Steuerung einer Buchung sollte nicht der GUI und damit im Verwaltungsmodell der Sekretärin obliegen.
Die Buchung sollte aber auch nicht von dem Bankkonto oder dem Schiff vorgenommen werden. Das Bankkonto sollte nichts von Kreuzfahrten wissen. Die Kreuzfahrt bean sollte nichts von Bankkonten wissen. Daher benötigt man für diese Aufgabe ein eigenes Objekt.
Die Aktivität, welche durch die session bean repräsentiert wird, ist in der Regel transient. Sie repräsentiert keinen Record der Datenbank. Jedoch erzeugt die session bean z.B. eine Reservierung, die einem customer eine ganz spezielle cabin auf einer ganz speziellen cruise zuweist. Diese Aktionen führen zu Änderungen der entity beans und diese Änderungen werden durch die Datenbank reflektiert.
Client applications verwenden die cruise bean oder die customer bean in unterschiedlicher Weise. Manche Verwendungen sind vorhersagbar, die meisten nicht. Darüberhinaus ändert sich die Verwendung mit der Zeit. Um nicht die cruise oder customer bean permanent ändern und anpassen zu müssen, ist es wichtig, die Daten von dem workflow zu trennen.
Die wesentliche Unterscheidung zur entity bean besteht darin, dass eine entity bean einen persistenten Status hat, wohingegen eine session bean Interaktionen modelliert und keinen persistenten Status hat.
Wenn man an ein Theaterstück denkt, repräsentieren die entity beans die Schauspieler und die session bean das Manuskript.
Die Verschiebung der business logic in session beans führt zu thin clients und reduziert den Netzverkehr. Ein Request des Client führt zu einem Methodenaufruf der session bean und vielen Methodenaufrufen für weitere session und entity beans, die in der Regel ebenfalls auf dem server liegen. So wird das Netz nur durch einen Methodenaufruf belastet. Ferner benötigt der Client nur eine einzige Netzverbindung zu der einen session bean, z.B. dem TravelAgent.
5 The bean container contract
Enterprise beans kommunizieren mit dem EJBServer über ein wohldefiniertes Komponentenmodell.
Die Basis dieses Komponentenmodels bilden die EntityBean und SessionBean interfaces.
Die entityBean Schnittstelle umfasst etwa solche Methoden wie
ejbCreate()
ejbPostCreate()
ejbActivate()
ejbLoad()
ejbStore()
Sie dienen dazu, die bean durch den EJB Server über Events zu informieren, die seinen Lebenszyklus betreffen. Wenn der Zustand der bean keine Reaktion auf die Events erfordert, können die Methoden leer implementiert werden.
EJBContext ist ein interface, welches von dem container implementiert wird.
EntityBeans verwenden die abgeleitete Klasse EntityContext, session beans die abgeleitete Klasse SessionContext.
EJBContext beliefert die bean mit Informationen über die umgebende Welt.
6 Beispiel Cruise Interface and Class CruiseHome
import java.rmi.RemoteException;
public interface Cruise extends javax.ejb.EJBObject
{
public String getName( )throws RemoteException;
public void setName(String name)throws RemoteException;
public String getShip( )throws RemoteException;
public String getCruiseId( )throws RemoteException;
public void setShip(String ship)throws RemoteException;
public String getDate( )throws RemoteException;
public void setDate(String termin)throws RemoteException;
public int getMaxPerson()throws RemoteException;
public void setMaxPerson(int maxPers)throws RemoteException;
public int getDays()throws RemoteException;
public void setDays(int dauer)throws RemoteException;
}
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import java.util.Enumeration;
public interface CruiseHome extends javax.ejb.EJBHome
{
public Cruise create(String cruiseId, String cruiseName,String cruiseShip,String cruiseDate, int cruiseDays,int cruiseMaxPerson)
throws RemoteException, javax.ejb.CreateException;
public Cruise findByPrimaryKey(String cruiseId)
throws RemoteException, javax.ejb.FinderException;
public Enumeration findAll()
throws FinderException,RemoteException;
}
7 Classes and Interfaces
Um eine enterprise bean zu implementieren, muss man zwei Interfaces und zwei Klassen implementieren.
Remote interface
Dieses definiert die business methods des bean.
Home interface
Dieses definiert die life cycle methods : Erzeugen, Löschen und Finden einer bean.
Bean class
Diese Klasse implementiert die business methods.
Primary key
Diese Klasse definiert den primary key. Er wird verwendet, um eine spezifische bean zu finden.
Die Klasse liefert einen Pointer in die Datenbank und implementiert Serializable().
Basierend auf den Interfaces und der bean class wird eine Menge code generiert. Dieser code erzeugt z.B. die beans, speichert sie in der Datenbank, überträgt die Anforderungen ins Netz usw. Mit diesem generierten code werden die beiden interfaces imlementiert.
8 Implementation des remote interface
// Einfügen
Bild EJBObject
Auf der Client Seite wird das remote interface duch den stub implementiert.
Auf der Server Seite verpackt das sogenannte EJBObject die erzeugte bean class, im Beispiel die CruiseBean und erweitert deren Funktionalität. Dieses EJBObject wird generiert und basiert auf der bean class und den Informationen des sogenannten DeploymentDescriptors. Das EJBObject arbeitet mit dem Container zusammen.
Wenn ein client eine Methode des stubs aufruft, empfängt das EJBObject die Anforderung und delegiert sie an die bean instance. Das EJBObject kümmert sich dabei um transactions, security, concurrency.
Es existieren verschiedene vendorspezifische Strategien für die Implementierung des EJBObjects
Bild Implementation EJBObject
Sämtliche remote interfaces sind von EJBObject abgeleitet.
Der Client erhält vom home interface eine Referenz auf ein EJBObject.
GetEJBHome() ermöglicht dem Anwender, wieder eine Referenz auf das home interface zu erhalten, wenn das home interface nicht mehr direkt erreichbar ist.
GetPrimaryKey() ist für session beans bedeutungslos.
Der primary key kann mittels serialization in ein file geschrieben werden. Er kann verwendet werden um assoziations zwischen entity beans zu verwalten.
EJBObject.isIdentical() liefert true, wenn zwei Referenzen dieselbe bean repräsentieren.
Object.equals() leistet dies nicht, da diese Methode lediglich die beiden stubs vergleicht. Diese können verschieden sein, obwohl sie dasselbe bean object referenzieren. Stateless session beans vom selben Typ werden als gleich angesehen.
Für entity beans invalidiert die remove() Methode die remote reference und löscht die Daten aus der Datenbank. Session beans werden freigegeben.
9 Implementation des home interface
Das home interface liefert life-cycle operations und metadata. cruiseHome erweitert EJBHome um spezifiche create() und find() Methoden.
Die Klasse unterstützt den Container bei der Verwaltung des bean life cycle (vgl. weiter hinten). Sie ist verantwortlich für die Erzeugung und Vernichtung von beans.
Wenn z.B. eine create() Methode des home interfaces gerufen wird, erzeugt EJBHome eine Instanz des EJBObjects und koppelt eine Instanz der CruiseBean daran. Anschließend wird die Methode ejbCreate() der CruiseBean gerufen.
find() Methoden für session beans sind bedeutungslos. Sie repräsentieren keine Daten, daher gibt es nichts zu suchen.
Die find() Methoden werden im Falle von container managed entities vom EJBServer automatisch generiert. Im Falle von bean managed entities wird jeder Aufruf an eine korrespondierende Methode der bean instance delegiert.
Mittels JNDI (vgl. weiter hinten) erhält man eine remote reference in Form eines stubs zu dem EJB Home object, welches das home interface auf der Server Seite implementiert.
EJBHome.remove()
Das Entfernen einer entity bean mittels remove() hat das Löschen der Daten in der Datenbank zur Folge.
EJBHome.getEJBMetaData()
Diese Funktion liefert eine Instanz von EJBMetaData.
Die Klassen EJBObject und EJBHome werden beim Einfügen einer bean in einen Container basierend auf den Klassen des JAR Files generiert.
10 Beispiel deployment descriptor
Ejb-jar.xml
no description
Ejb1
no description
CruiseBean
Cruise
pCruise.CruiseHome
pCruise.Cruise
pCruise.CruiseBean
Container
java.lang.String
False
no description
cruiseName
no description
cruiseDate
no description
cruiseShip
no description
cruiseDays
no description
cruiseId
no description
cruiseMaxPerson
cruiseId
no description
ReservationBean
Reservation
pCruise.ReservationHome
pCruise.Reservation
pCruise.ReservationBean
Container
java.lang.String
False
no description
reservationId
no description
customerId
no description
cruiseId
reservationId
no description
CustomerBean
Customer
pCruise.CustomerHome
pCruise.Customer
pCruise.CustomerBean
Container
java.lang.String
False
no description
customerName
no description
customerFirstname
no description
customerId
customerId
no description
TravelAgentBean
TravelAgent
pCruise.TravelAgentHome
pCruise.TravelAgent
pCruise.TravelAgentBean
Stateless
Container
Cruise
Home
findAll
Required
Cruise
Remote
setMaxPerson
int
Required
Cruise
Home
create
java.lang.String
java.lang.String
java.lang.String
java.lang.String
int
int
Required
Cruise
Remote
getDays
Required
Cruise
Remote
getMaxPerson
Required
Cruise
Remote
getName
Required
Cruise
Home
findByPrimaryKey
java.lang.String
Required
Cruise
Remote
getShip
Required
Cruise
Remote
getDate
Required
Cruise
Remote
setName
java.lang.String
Required
Cruise
Remote
remove
Required
Cruise
Remote
setShip
java.lang.String
Required
Cruise
Remote
setDays
int
Required
Cruise
Remote
setDate
java.lang.String
Required
Cruise
Remote
getCruiseId
Required
Reservation
Home
findAll
Required
Reservation
Remote
getReservationId
Required
Reservation
Home
create
java.lang.String
java.lang.String
java.lang.String
Required
Reservation
Remote
getCustomerId
Required
Reservation
Home
findByPrimaryKey
java.lang.String
Required
Reservation
Remote
getCruiseId
Required
Customer
Remote
setName
java.lang.String
Required
Customer
Remote
getFirstname
Required
Customer
Remote
setFirstname
java.lang.String
Required
Customer
Remote
getName
Required
Customer
Home
create
java.lang.String
java.lang.String
java.lang.String
Required
Customer
Home
findByPrimaryKey
java.lang.String
Required
TravelAgent
Remote
getRecordReservation
java.lang.String
Required
TravelAgent
Remote
travelAgentReservation
java.lang.String
java.lang.String
java.lang.String
Required
11 Deployment descriptors and JAR files
Deployment descriptors sind Klassen, die ähnlich Property files die Anpassung des Verhaltens (SessionBean or EntityBean, security, transactions) einer enterprise bean erlauben, ohne die bean class oder die interfaces ändern zu müssen.
Für jede bean wird ein deployment descriptor erzeugt und mit den Daten der bean gefüllt. In der Regel setzt der Anwender die Attribute der deployment descriptor class im Rahmen des Integrated Development Environment (IDE) oder des Deployment Tools mittels Property Sheet. Das deployment descriptor object wird dann serialisiert. Der deployment descriptor kann von Anwendern der bean modifiziert werden.
Das deployment descriptor file ist Bestandteil des JAR Files der bean.
Das JAR File enthält die bean class, remote interface, home interface, primary key, manifest, serialiserter deployment descriptor.
JAR`s manifest ist das Inhaltsverzeichnis des JAR Files.
Das Element beschreibt unter anderem die zugehörige DTD Datei. Diese wird zur Überprüfung der Struktur des Dokumentes verwendet.
Die übrigen Elemente sind inneinander verschachtelt und jeweils durch ein Start- und Ende-Tag begrenzt.
Alle Elemente müssen in enthalten sein.
Alle beans sind im Element enthalten.
In einem Element definierte beans sind entity beans.
In einem Element definierte beans sind session beans.
Die führen alle container managed fields in der entity bean Klasse auf. Diese Felder werden in der Datenbank gespeichert und vom container zur Laufzeit verwaltet.
Das Element beschreibt die Sicherheitsrollen und Transaktionsattribute der beans.
Das Element beschreibt, dass Transaktionen „required“ werden.
4 Example Cruise
nur elektronisch
5 Container and Bean Managed Persistence of Entity Beans
1 Container Managed Persistence
Der container weiß, wie die Attribute der bean auf die Felder der Datenbank abgebildet werden und kümmert sich um das Einfügen, Updaten und Löschen.
Die bean kann unabhängig von der Datenbank formuliert werden. Dies erhöht die Wiederverwendbarkeit.
2 Bean Managed Persistence
Der Entwickler der bean muss den Code zur Manipulation der Daten in der Datenbank schreiben.
Er erhält auf diese Weise eine größere Flexibilität. Jedoch bindet ihn diese Vorgehensweise in stärkerem Maße an die spezifische Datenbank.
6 Stateful and stateless session beans
1 Stateful session beans
Eine stateful session bean verwaltet einen Status in Bezug auf den client.
Z.B. könnte man bei der TravelAgent bean zunächst mittels setCustomerId(), setCruiseId() remote Referenzen zu Customer und Cruise beans erzeugt. Der nachfolgende parameterlose createReservation() Aufruf funktioniert dann nur, wenn die erforderlichen Referenzen vorher gesetzt wurden. Nach dem Setzen der Customer und Cruise Referenzen befindet sich der TravelAgent daher in einem anderen Status.
Dieser Status wird als conversational state bezeichnet, da er die Conversation zwischen bean und client repräsentiert.
Bei einer stateful session bean ändert sich der Zustand der bean im Verlaufe der Kommunikation mit dem Client. Damit sind die Methodenaufrufe nicht mehr voneinander unabhängig.
Stateful session beans können nicht parallel mit verschiedenen clients kommunizieren.
Eine stateful session bean ist die Erweiterung der client application. Sie führt Aufgaben im Auftrag des client aus. Sie arbeitet als Agent des client.
Jeder Methodenaufruf des client wird daher im Prinzip von derselben instance der bean bedient.
Stateful session beans kapseln den conversational state und die business logic auf dem server. Die Auslagerung der business logic auf den server führt zu einem thin client und erleichtert die Verwaltung des Systems. Die Verwaltung des workflow im Auftrag des client führt zu einem interface, welches z.B. direkte Datenbankzugriffe vor dem client verbirgt.
Stateful session beans sind nicht persistent. Sie sind während der gesamten Lebensdauer einem einzigen client zugeordnet und haben eine vorgegebene timeout Zeit. Bei jedem Aufruf einer business Methode wird der timeout zurückgesetzt. Wenn der client die bean vor dem Ablauf des timeout nicht mehr verwendet, wird die instance zerstört und die remote reference ungültig. Der client kann die session bean natürlich auch mittels remove() explizit zerstören.
Stateful session beans können nicht konkurrierend verwendet werden. Im Falle einer entity bean werden alle client Anforderungen durch dasselbe EJB object koordiniert. Im Falle von session beans ist das EJB object genau einem client zugeordnet.
2 Stateless session beans
Stateless session beans verwalten keine eigenen conversational states (vgl. aktuelle TravelAgent Implementierung). Die Methoden sind völlig unabhängig voneinander.
Wenige stateless session beans können daher tausende von Clients parallel bedienen.
Viele EJB objects könne wenige Instanzen von stateless session beans nutzen. Stateless session beans könne frei zwischen verschiedenen EJB object geswapped (vgl. weiter hinten) werden.
Die beans können sich von einem Aufruf einer Methode zum nächsten keine Informationen merken. Jedoch bedeutet dies nicht, dass die beans keine Attribute haben dürfen. Eine stateless bean kann z.B. eine Referenz auf ein Debugfile halten. Der Ablauf der Methoden darf jedoch nicht von dem Wert der internen Attribute abhängen. Die internen Attribute sind für den Client nicht sichtbar. Der client kann nicht davon ausgehen, beim nächsten Aufruf von derselben bean bedient zu werden.
Die Eigenschaft stateless wird über eine Property der SessionDescriptor Klasse definiert.
7 Resource Management
1 Problem
EJBServer müssen so konstruiert sein, dass sie ggf. Millionen von beans verwalten können.
2 Pooling
Eine Technik zur Verbesserung der Performance besteht darin, einen pool der benötigten Instanzen der beans anzulegen. Clients werden dann aus diesem Pool bedient. Es ist sehr viel weniger aufwendig, Instanzen aus diesem Pool wiederzuverwenden als permanent neue Instanzen zu erzeugen und wieder zu löschen. Ein EJBServer verwaltet instance pools für jeden Typ von bean.
Da die Clients ohnehin nie direkt auf die beans zugreifen können sondern nur über wrapper EJB objects, besteht keine Notwendigkeit, für jeden der Millionen Clients eine bean im Speicher bereit zu halten. Der EJBServer hält in seinem Pool stattdessen eine viel kleinere Zahl von beans zu Erledigung der von den Clients anfallenden Aufträge.
Die Größe des pools kann darüberhinaus dem aktuellen Bedarf angepasst werden.
3 Swapping
Während Pausen können bean instances an den pool zurückgegeben werden. Man spricht in diesem Falle von instance swapping.
Im Falle von stateless session beans bereitet es keine Probleme, wenn ein Client zwischen zwei Methodenaufrufen eine andere bean instance zugewiesen bekommt.
[pic]
Bild Swapping
Entity beans partizipieren ebenfalls an dem Mechanismus des instance swapping. Sie haben keinen conversational state, der zwischen zwei Aufrufen gemerkt werden muss. Ihr Status in Form der Daten ist direkt in der Datenbank gespeichert.
4 The Life Cycle of an Entity Bean
1 States
EJB definiert die states der entity bean über die Beziehung zum instance pool.
// Einfügen
Bild Zustände der bean
2 Does Not Exist
Beginn und ende des life cycles
Die bean ist nicht instanziert.
Die bean ist eine Sammlung der Files, die zur Entwicklungszeit der bean erzeugt wurden, z.B. bean, remote interface, home interface, serialiserter EntityDescriptor usw.
3 Pooled
Die bean ist Instanziert aber noch nicht mit einem EJBObject assoziiert.
Wenn der EJB Server gestartet wird, instanziiert er mehrere Instanzen der bean und plaziert sie in dem instance pool.
Die Instanzen werden mittels Class.newInstance() erzeugt. newInstance() verwendet den Default-Konstruktor ohne Argumente. Damit erhalten die persistent fields ihre Default-Werte. Die Instanz repräsentiert in diesem Zustand keine Daten der Datenbank.
Direkt nach der Erzeugung und vor dem Einfügen in den pool wird der bean mittels setEntityContext() ihr context zugewiesen. Die beans in dem instance pool sind inaktiv. Es sei denn sie werden für einen find() request verwendet. find() requests erfordern keinen definierten Zustand der bean.
4 Ready
Die bean ist instanziert und mit einem EJBObject assoziiert.
Die bean instance ist ready für Anforderungen über Methodenaufrufe.
1 Transition from pooled to ready via creation
// Einfügen
Bild Pool
Wenn ein client die create() Methode von EJB home aufruft, wird ein EJB object auf dem server erzeugt, diesem EJB object eine bean des instance pools zugewiesen, die zur gerufenen create() Methode gehörende ejbCreate() methode der bean instance gerufen, ein primary key erzeugt und in das EJB object eingebettet. Durch den primary key erhält das EJB Object seine Identität.
Im Falle von bean managed persistence (vgl. weiter hinten), erzeugt die bean selbst den primary key im Rahmen von ejbCreate() und liefert ihn an den EJB Server zurück.
Nach dem Ablauf der ejbCreate() methode steht die Context Information zur Verfügung.
Anschließend wird die ejbPostCreate() Methode aufgerufen. In dieser Methode kann die bean Initialiserungen vornehmen, welche auf dem Kontext basieren.
Schließlich übermittelt der EJB Server dem client ein stub object als remote reference.
Damit geht die zugewiesene bean instance in den ready state über. Die bean instance kann nun requests des client und callbacks des EJBServers empfangen.
2 Transition from pooled to ready via activation
Activation ermöglicht wenigen bean instances viele clients zu bedienen. Wenn eine bean instance zum pool zurückkehrt, hinterlässt sie das EJB object ohne instance. Das EJB object unterhält seine Verbindung zum stub auf client Seite. Aus der Sicht des client hat sich daher die entity bean nicht verändert. Sowie der client eine business methode aufruft, erhält das EJB object eine neue bean instance.
Wenn also eine bean instance aktiviert wird, verlässt sie den instance pool und wird einem EJB object zugewiesen. Nach der Zuweisung an ein EJB object ist der EntityContext in der Lage, Informationen zu liefern, die spezifisch für das EJB object sind. Direkt nach der Zuweisung wird vom EJB Server die ejbActivate() Callback-Methode gerufen. Im Rahmen dieser Methode kann die bean Arbeiten verrichten, die für die Bedienung des Client erforderlich sind.
Im Falle von container managed beans werden anschließend die persistent fields vom container aus der Datenbank gefüllt. Anschließend wird die bean mittels ejbLoad() über diesen Vorgang informiert.
Im Falle von bean menaged persistence hat die bean selbst im Rahmen von ejbLoad() ihre persistent fields zu füllen.
3 Transition from ready to pooled via passivation
Passivation ist der Prozess der Trennung der bean instance von ihrem EJB object. Der container kann die Trennung zu jeder Zeit vornehmen, es sei denn, die instance führt gerade eine Methode aus. Als Teil des passivation process ruft der container die callback-Methode ejbPassivate() auf. Auf diese Weise erhält die bean Gelegenheit, Resourcen frei zu geben.
Die Methode dient nicht zum Abspeichern der persistent fields im Falle von bean managed persistence. Diese Abspeicherung erfolgt im Rahmen der Methode ejbStore(), die vor ejbPassivate() aufgerufen wird.
4 Transition from ready to pooled via removal
Wenn der client die bean nicht mehr benötigt, ruft er eine remove() Methode für die bean. Dies hat zur Folge, dass die bean instance von dem EJBObject disazzoziiert und an den instance pool zurüchgegeben wird.
Eine bean instance gelangt also auch in den pooled state, wenn der client eine der remove() Methoden aufruft.
Im Falle von entity beans werden die Daten aus der Datenbank entfernt.
Die Information bezüglich der Identität der bean ist während der Ausführung der callback-Methode ejbRemove() noch über den EntityContext erreichbar. Die ejbRemove() Methode muss alle Resourcen frei geben, die auch im Rahmen von ejbPassivate() freizugeben sind. Z.B. kann man die ejbPassivate() Methode aus der ejbRemove() Methode aufrufen.
5 Anmerkungen
ejbLoad() und ejbStore() können nur im ready state gerufen werden. Manche EJB Server rufen vor jedem Methodenaufruf ejbLoad() und nach jedem Methodenaufruf ejbStore(). Die callBack-Methode ejbLoad() wird im Falle von container managed beans direkt nach dem Laden der persistent fields aus der Datenbank gerufen und die Methode ejbStore() direkt vor dem Speichern der persistent fields in die Datenbank.
Die callback-Methoden ejbCreate() und ejbRemove() können verwendet werden, um Daten in die Datenbank einzufügen bzw. zu löschen.
Das Leben einer bean instance endet, wenn der container die Instanz aus dem pool entfernt und dem Garbage Collector übergibt. Dies geschieht z.B., wenn der container die Anzahl der bean instances dem Bedarf anpasst, wenn der EJB Server heruntergefahren wird, wenn eine bean sich unnormal verhält.
Mitels der callback-Methode unsetEntityContext() informiert der container die bean über die bevorstehende Zerstörung. Vor der garbage collection wird schließlich noch wie für jedes andere java object die finalize() Methode gerufen.
5 The Life Cycle of a Stateless Session Bean
1 States
[pic]
Bild life cycle stateless session bean
2 DoesNotExist
Es existiert keine Instanz im Speicher
3 Method-Ready Pool
Der container wird in Abhängigkeit vom Bedarf Instanzen erzeugen oder vernichten.
1 Transition to the Method-Ready Pool
Die bean wird mittels Class.newInstance() instanziiert. Dies erfordert einen Konstruktor ohne Argumente. Die Initialisierung findet im Rahmen von ejbCreate() statt.
Mittels setSessionContext() wird die Referenz auf den EJBContext gesetzt.
Die ejbCreate() Methode wird gerufen. Dies geschieht nur einmal während der Lebensdauer der bean. Im Unterschied zu entity beans und stateful session beans wird ein create() Aufruf des client nicht an die Methode ejbCreate() der bean delegiert.
Eine Socket-Verbindung kann also z.B. im Rahmen von ejbCreate() geöffnet, während der gesamten Lebensdauer beibehalten und im Rahmen von ejbRemove() geschlossen werden.
2 Life in the Method-Ready Pool
Wenn ein client eine business Methode ruft wird dieser Aufruf an irgend eine verfügbare Instanz im Method-Ready Pool delegiert. Während die Instanz die Anforderung ausführt, ist sie für keinen weiteren Aufruf verfügbar. Stateless session beans werden einem EJB object lediglich für die Dauer eines einzigen Methodenaufrufes zugewiesen.
Es ist nicht vorhersehbar, welche instance die Anforderung des client bedient.
Die callback-Methoden ejbActivate() bzw. ejbPassivate() werden im Falle einer stateless session bean nicht aufgerufen. Ein create() Aufruf des client führt nicht zu einem Aufruf von ejbCreate().
3 Transition out of the Method-Ready Pool
Eine stateless session bean stirbt, wenn der Server entscheidet, die Anzahl der Instanzen zu reduzieren.
An dieser Stelle wird die callback-Methode ejbRemove() gerufen, in der die bean ihre Resourcen freigeben kann.
Ein remove() Aufruf des client bewirkt hingegen nur die Freigabe des stubs und des EJB objects. Nach der ejbRemove() methode wird die bean dem garbage collector übergeben.
Im Falle einer stateless session bean wird also durch eine timeout oder remove() Operation lediglich die remote reference zerstört jedoch nicht die bean instance. Diese wird für andere clients weiterverwendet.
6 The Life Cycle of a Stateful Session Bean
1 The Activation Mechanism
Der größte Unterschied zu den anderen beans besteht darin, dass kein instance pooling betrieben wird. Stateful session beans sind während ihres gesamten Lebens einem client gewidmet.
An Stelle des pooling werden sie lediglich aus dem Speicher entfernt, um Resourcen zu sparen.
Eine solche bean passivated und wieder activated.
[pic]
Bild activation of stateful session beans
Zu diesem Zweck wird im Rahmen der Passivierung deren conversational state z.B. mittels Java Serialiserung gespeichert, dem EJBObject zugeordnet und die stateful session bean instance zerstört.
Dabei bleibt das EJB object mit dem client verbunden. Die bean instance wird jedoch dem garbage collector übergeben.
Im Rahmen der Aktivierung wird eine neue Instanz der session bean erzeugt, dem EJBObject zugeordnet und mit den gespeicherten Daten gefüllt.
Die callback Methoden ejbActivate() und ejbPassivate() informieren die bean über die Vorgänge und erlauben der bean notwendige Arbeiten vor der Passivierung oder nach der Aktivierung durchzuführen.
2 States
[pic]
Bild States of a Stateful Session Bean
3 Does Not Exist
Es existiert keine Instanz im Speicher
4 Method Ready
1 Transition to Method Ready
Nach dem Aufruf der create() Methode erzeugt der container mittels newInstance() eine bean instance, weist diese einem EJB object zu, setzt mittels setSessionContext() den EntityContext und ruft ejbCreate(). Nach der Rückkehr dieser Funktion liefert er eine remote reference an den client.
Nun ist die bean bereit, Aufrufe des client zu bedienen.
2 Transition to Does Not Exist
Der Übergang in den Does not Exist state findet statt, wenn die bean removed wird. Der Anwender kann eine der remove() Methoden aufrufen. Der Container kann die bean entfernen, wenn ein timeout abgelaufen ist. Die bean erhält die Gelegenheit, im Rahmen von ejbRemove() Resourcen zu schließen oder referenzierte beans zu entfernen.
5 Passivated
1 Transition to Passivated
Wenn eine stateful session bean in diesen Zustand versetzt wird, werden die instance fields der bean gelesen und auf ein sekundäres Speichermedium geschrieben, welches dem EJB object zugeordnet ist.
Mittels der callback-Methode ejbPassivate() wird die bean darüber informiert, dass sie in den passiven Zustand versetzt wird. Die bean sollte im Rahmen dieser Funktion alle offenen Resourcen schließen und ihre nontransient, nonserializable fields auf 0 setzen.
Der conversational state muss aus primitve values oder serializable objects bestehen. Der container kann dazu den Java Serialisierungsmechanismus nutzen. Referenzen auf andere beans und die Referenz auf den SessionContext werden vom container gespeichert und bei der Aktivierung wieder restauriert.
Nichtserialisierbare Objektreferenzen und transiente variablen bleiben nicht erhalten.
Felder, die nonserializable und nontransient sind müssen auf 0 gesetzt werden.
2 Transition to Method Ready
Wenn der client eine Methode einer passivated bean ruft, wird diese aktiviert. SessionContext und bean referenzen werden restauriert. Anschließend wird die bean mittels ejbActivate() über die Aktivierung informiert. Die bean kann im Rahmen dieser Methode Resourcen öffnen und transiente Felder initialisieren. Im Gegensatz zum Java Serialisierungsstandard werden transiente Felder nicht auf Default-Werte gesetzt. Sie können nach der Aktivierung beliebige Werte enthalten uns sollten daher im Rahmen von ejbActivate() sinnvoll initialisert werden.
8 Primary Services
1 Overview
Die OMG hat 13 frameworks definiert, die als services bezeichnet werden. 6 von diesen werden als primary services bezeichnet. Diese sind erforderlich für einen effektiven Betrieb des EJBServers.
Die primary services umfassen concurrency, transactions, persistence, distributed objects, naming and security.
Einige dieser services werden im Folgenden ausführlicher dargelegt.
2 Concurrency
Session beans
Eine stateful session bean verwaltet conversational states. Daher wäre ein konkurrierender Zugriff durchaus problematisch. Jedoch wird sie nur von demjenigen client verwendet, der sie auch erzeugt hat. Daher muss sie nicht geschützt werden. Eine stateful session bean ist immer genau einem client zugeordnet.
Eine stateless session bean hat keine inneren Zustände. Daher bedarf sie ohnehin keines Schutzes.
Entity beans
Entity beans repräsentieren Daten, z.B. eine Kreuzfahrt. Diese Daten werden durchaus von mehreren clients, z.B. Reisebüros, konkurrierend angefasst. Entity beans sind daher shared components.
EJB verhindert den konkurrierenden Zugriff auf eine bean instance. Verschiedene clients können sehr wohl mit ein und demselben EJBObject verbunden sein. Jedoch hat nur ein client thread Zugriff auf die bean instance. Wenn also ein Client eine Methode der bean aufruft, hat ein anderer client erst dann wieder die Möglichkeit für einen Zugriff, wenn die Methode beendet wurde. Ein entsprechender Schutz existiert über einzelne Methodenaufrufe hinaus für Transaktionen.
// Einfügen
Bild Concurrent Access
Bedingt durch diese automatische Behandlung des konkurrierenden Zugriffs müssen die bean Methoden nicht thread-safe gemacht werden.
Die EJB Spezifikation verhindert sogar die Verwendung des Schlüsselwortes synchronized.
Achtung :
Bean instances sind nicht reentrant-fähig. Ein loopback tritt auf, wenn eine bean A eine Methode von bean B ruft und dann B versucht einen callback zu A zu machen. Bean A unterscheidet nämlich nicht zwischen dem callback von B (reentarnt code) und einem Versuch eines anderen Clients, eine Methode aufzurufen (multithreaded access).
Die fehlende reentrant-fähigkeit bezieht sich nur auf die Kommunikation mit anderen beans.
// Einfügen
Bild reentrant
3 Persistence
Der Status einer entity bean wird permanent in einer Datenbank gespeichert.
Wenn der container für die Synchronisation der bean felder mit den Datenbankeinträgen verantwortlich ist, spricht man von container managed persistence. Im anderen Falle spricht man von bean managed persistence.
Object to relationale persistence
Die entity bean fields werden auf Tabellen und spalten einer relationalen Datenbank abgebildet.
[pic]
Bild Mapping Attributes to Relational Database
Die Anforderung an EJBHome zur Erzeugung oder Vernichtung einer entity bean hat die Erzeugung oder Vernichtung eines Datenbank-Records zur Folge.
Java objects lassen sich nicht immer nahtlos auf relationale Datenbanktabellen abbilden.
Object database persistence
Objektorientierte Datenbanken speichern Objekt Typen und Objekt Graphen. In einer OO Sprache geschriebene Komponenten lassen sich sauberer auf die Datenbank abbilden. Diese Datenbanken handeln zirkulare Referenzen.
Jedoch sind Objektdatenbanken noch nicht soweit standardisiert wie relationale und es existieren weniger third-party Produkte zur Unterstützung.
4 Distributed Objects
Beispiel für ein remote interface
public interface TravelAgent extends EJBObject {
public Reservation travelAgentReservation(String idRes, String idCruise, String idCustomer)
throws RemoteException, DuplicateKeyException, CreateException;
public Reservation getRecordReserv(String idRes) throws RemoteException;
}
RMI fordert, dass alle Parameter und Returnwerte entweder primitive Jafa Typen (int, double, byte ...) sein müssen oder aber serialisierbare Objekte. Serialisierbare Objekte können über das Netz auf einen remote Computer kopiert werden.
Änderungen an der remote Kopie werden im Orginal nicht reflektiert.
[pic]
Bild passing serializable objects
Achtung : Rechts muss stehen
Copy of serializable object
Über diese Technik kann man auch remote referenzen auf beans übertragen. Eine remote reference ist ein Remote interface, welches durch ein EJBObject als stub implementiert wird. Wenn eine solche remote reference als Parameter oder Returnwert einer Methode verwendet wird, so wird der stub per kopie über das Netz transportiert. Die Kopie des EJBObject stub zeigt auf dieselbe bean. Genaugenommen zeigt der stub auf das Wrapper EJBObject der bean.
[pic]
Bild passing remote references
5 Naming
Locating Beans with JNDI
Beispielcode für die Verwendung einer bean.
InitialContext initial = new InitialContext();
Object objref = initial.lookup("reservation");
...
record = hreservation.findByPrimaryKey(idHilfe);
...
theReservation = hreservation.create(idReservation, idCruise, idCustomer);
Die Referenz auf EJBHome für das cruise bean erhält man über die Verwendung des Java Naming and Directory Interface (JNDI).
JNDI erlaubt einem application client, den EJBServer als eine Menge von directories wie in einem gewöhnlichen File System zu sehen.
Der initial context ist der Startpunkt der Suche, vergleichbar mit der root eines File Systems.
Alle EJBServer müssen unabhängig vom Hersteller den JNDI Zugriff für ihren speziellen naming und directory service unterstützen.
-----------------------
Heinrich
Lina
Peter
Jens
Renate
Christian
................
................
In order to avoid copyright disputes, this page is only a partial summary.
To fulfill the demand for quickly locating and searching documents.
It is intelligent file search solution for home and business.
Related searches
- software development business plan template
- software for small business management
- software company business plan template
- software company business plan
- business plan pro software download
- crm software for small business
- crm software features
- treasury management software vendors
- business plan software free
- free business plan software online
- list of education software companies
- stock screening software reviews