Software Engineering Einführung Definition Software Engineering ist das technische und planerische Vorgehen zur systematischen Herstellung und Pflege von Software, die zeitgerecht und unter Einhaltung der geschätzten Kosten entwickelt bzw. modifiziert wird. Gründe - komplexer Code nur vom Urheber überschaubar Kosten kaum kalkulierbar Systementwicklung vollkommen unsystematisch Unplanbarkeit und Unkalkulierbarkeit der Softwareprojekte Hohes Spezialistentum der „Informatiker“ —>es fehlten in der Informatik strukturierende und ordnende Verfahren und Methoden Bereiche - Anforderungsanalyse – Was möchte ich machen / Ziel ? Entwurf – Wie soll es gemacht werden? Planung – Zeit, Budget, Menschen Durchführung - Programmierung / Integration / Dokumentation Testing – Funktionsprüfung, Fehlerbeseitigung Rollout – Einführung + Schulung Pflege – Weiterentwicklung + Anpassung Ziel war die Beherrschbarkeit der Softwareentwicklung. Prinzipen Im Angriff essen Rehe eine Nase. Requirements Engineering Definition - Anforderungen an ein System beschreiben • diejenigen Dienste, die von einem potentiellen Kunden von einem System erwartet werden • die Gegebenheiten, unter denen das System entwickelt und betrieben wird Anforderungsanalyse Heißt der Prozess des - Herausfinden (Elicitation) Analysierend (Analysis) Dokumentierend (Specification) Überprüfen (Validation) Anforderungen erfüllen zwei Aufgaben - Basis für eine Ausschreibung für eine Software - Das Erledigen der Aufgabe muss interpretierbar sein - Basis für einen Vertrag über eine Software-Entwicklung - Die Aufgaben des Software müssen detailliert festgelegt sein Typische Problemstellungen und Begrifflichkeit - Anforderungen, Lastenheft, Pflichtenheft, Spezifikation - Zielgruppen - Typen von Anforderungen - Funktionale vs. nicht-funktionale Anforderungen - Prozesse - Management von Anforderungen Designprinzipien Ziel - Einfach zu wartender Code - Einfach zu erweiternder Code - Möglichst geringe Fehlerrate Abstraktion - der Prozess, Informationen zu vergessen - Abstraktion durch Parametrisierung, durch Spezifikation - Prozedurale Abstraktion, Datenabstraktion, Kontrollabstraktion Lose Kopplung - Zusammenhang/Abhängigkeiten zwischen Komponenten möglichst gering Dekomposition und Modularisierung - Zerlegen eines großen Systems in kleinere, beherrschbare Einheiten - Verteilen von Funktionalitäten Kapselung, Information Hiding - Verbergen der Details einer Komponente - Unzugänglich machen der Details (Public, Private) Trennen von Schnittstelle und Implementierung (?) - Komponenten durch ihre Schnittstellen spezifizieren Vollständigkeit und Primitivität - Komponenten enthalten alles, was zu einer Abstraktion gehört, aber nicht mehr Design Pattern Template Pattern - Um Codeverdopplungen vorzubeugen werden die nichtveränderlichen Anteile eines Algorithmus festgelegt - Veränderbare Teile werden durch Unterklassen implementiert - (Methoden in der Oberklasse (abstrakt) initialisieren und per Polymorphe in den Unterklassen ändern) Strategy Pattern - Verwenden wenn sich viele verwandte Klassen nur in ihrem Verhalten unterscheiden Ein Algorithmus zur Laufzeit austauschbar sein muss Algorithmen vor den Konsumenten verborgen werden sollen Beispiel: Datei in unterschiedlichen Dateiformaten speichern (pdf, png, jpg) Dependency Injection Factory Pattern - Trennung Objektverarbeitung / Objekterstellung - Komplexe Erstellungsprozesse - Unbekannte konkrete Objekte Repository Pattern - Abfragen zentralisieren Abfragen wiederverwenden Datenzugriff abstrahieren Testen erleichtern UML Unified Modeling Language Es musste eine Notationsmöglichkeit für Softwareentwürfe gefunden werden, welche folgendes ermöglicht: - Struktur und Verhalten einer Applikation visuell darstellen - Assoziation („interagieren miteinander“) - Beziehungen zwischen verschiedenen Objekten - Komposition („hat ein - Beziehung“) - ein Teil immer nur in genau einem zusammengesetzten Objekt - Beispiel: Beziehung zwischen einer Bestellung und den einzelnen Posten der Bestellung. Ein Bestellungsposten gehört in genau eine Bestellung, und wird die Bestellung gelöscht, löscht man automatisch auch alle ihre Posten. - Vererbung („ist ein - Beziehung“) - ein Objekt ein Teil von mehreren zusammengesetzten Objekten - Beispiel: Beziehung zwischen einer Mannschaft und ihren Spielern. Ein Mensch kann in mehreren Mannschaften spielen, und wird eine Mannschaft aufgelöst, bedeutet es in den allermeisten Fällen nicht das Ende für ihre Ex-Spieler. - Kardinaltität - Angabe der Art der Assoziation: Kann- oder Muss-Beziehung - Mit wie vielen Objekten eine Beziehung eingegangen werden kann, muss auch noch angegeben werden Aufbau - Klassen werden als Rechtecke dargestellt - Drei Bereiche werden unterschieden - Oberer Bereich: Name der Klasse - Mittlerer Bereich: Eigenschaften der Klasse - Unterer Bereich: Methodendeklarationen - Eine Klasse in der UML ist nichts anderes als eine Klasse in einer objektorientierten Programmiersprache - Statische Klassen werden unterstrichen Sichtbarkeit von Variablen und Methoden - - private + public # protected ~ package Das Klassendiagramm Stereotypen - Erweitern die UML - Können Eigenschaften und Merkmale festlegen - Notation: <<stereotype>> Ziel - Komplexe Softwareapplikationen im Vorhinein detailliert zu planen und zu dokumentieren - In großen Teams gemeinsam nach einem Plan zu arbeiten - Beispiel: Der Softwareentwurf ist vergleichbar mit dem Architekturentwurf eines Hauses und legt das gesamte weitere Vorgehen detailliert fest Model View Controller Ziel - Trennung von Oberfläche und Businesslogik Implementierung der Ausgabe für verschiedene Endgeräte/Plattformen Bessere Wartbarkeit Übersichtlicherer Code Aufbau Model - Enthält die darzustellenden Daten - Gegebenenfalls auch die Geschäftslogik - Unabhängig von Präsentation und Steuerung - Änderungen im Model werden durch das Beobachter Entwurfsmuster mitgeteilt View - stellt die benötigten Daten aus dem Model dar - Nimmt Benutzeraktionen entgegen - Verarbeitet diese jedoch nicht weiter! - Kennt sowohl ihren Controller als auch ihr Model Controller - Verwaltet eine oder mehrere Views - Nimmt Benutzeraktionen entgegen, - Wertet diese aus - Agiert entsprechend - zu jeder Präsentation existiert ein Controller - Manipuliert Daten im Model, je nach Benutzereingabe Test Driven Development - Perspektivenwechsel - Erst der Test, dann die Implementierung - Vorteile - Weniger überflüssiger Code - Einfacheres Refactoring - Funktionierende Unittests - Bessere Codequalität - Besseres Verständnis der Businesslogik Konkreter Einsatz in 5 Schritten 1. Test für eine Methode erstellen - Der Entwickler entwirft zunächst den Test - Dies führt dazu, dass sich der Entwickler zunächst damit auseinandersetzt, welches Ergebnis sein Code erzeugen soll 2. Überprüfen, dass der Test negativ ausfällt - Sicherstellen, dass der Test korrekt erstellt wurde - Ein Test, der bereits ein positives Ergebnis zurückliefert, ohne dass eine Implementierung vorliegt, ist offensichtlich falsch 3. Funktionalität implementieren - in diesem Schritt wird die eigentliche Funktionalität implementiert. Dabei sollte darauf geachtet werden, dass nur so viel Code geschrieben wird, wie für ein positives Testergebnis benötigt wird. 4. Implementierung mit dem erstellten Test validieren Die erzeugten Implementierung wird mit dem zu Beginn geschriebenen Code validiert. Bei positivem Ergebnis geht es mit dem nächsten Schritt weiter. Bei einem negativen Ergebnis wird die Implementierung solange angepasst, bis es zu einem positiven Ergebnis bei der Vailidierung kommt. 5. Nötige Restrukturierungen am Quellcode durchführen Im letzten Schritt werden Verbesserungen an der Implementierung durchgeführt, die z.B. durch das Zusammenfassen verschiedener Codeabschnitte erreicht werden können. Nach jeder Restrukturierung wird anhand der bisher erstellten Tests sichergestellt, dass keine bereits implementierte Funktionalität beeinträchtigt wurde. - Danach wird mit Schritt 1 die nächste Integration begonnen, bis sämtliche Anforderungen an die Applikation implementiert sind Source Control Management Grundlagen Diverse mögliche Bezeichnungen, z.B.: - Version control - Source control - Revision control Sinn & Zweck - Integraler Bestandteil eines Entwicklungsprozesses - Ermöglicht - Entwicklern Arbeitsschritte schnell zu kommunizieren - Quellcode dem ganzen Team zur Verfügung zu stellen - „fertige“ Versionen des Quelltextes bereitzuhalten - Die gleichzeitige Entwicklung unterschiedlicher Features - Eine Historie über alle veränderten Dateien - diverse unterschiedliche Systeme stehen zur Verfügung, z.B.: - Subversion - CVS - Mercurial - Darcs - Git GIT Repository - enthält alle Dateien und deren unterschiedliche Versionen sowie zusätzliche Metadaten Ähnlich einer Datenbank Kann jede Version einer Datei zurückgeben Kann eine Historie zu jeder Datei zurückgeben Kann eine Historie über das gesamte Projekt zurückgeben Benutzer können - eine aktuelle Kopie aller Dateien abrufen - Diese Dateien ändern - Einen Commit durchführen - Dadurch werden alle Änderungen zurück ins Repository gespeichert, inklusive Metadaten (Was wurde geändert und wer war es?) Commit - Speichervorgang der aktuellen Änderungen in das Repository - Benutzerkommentar möglich Push - speichert Änderungen eines lokalen Repositories in ein Remoterepository Pull - Überträgt Änderungen eines Remoterepositories in ein lokales Repository Merge - Das Zusammenführen von Änderungen - Jede Änderung in jeder Datei wird vom Versionskontrollsystems überwacht - Beim Merge werden nun alle Änderungen zusammengeführt und mögliche Konflikte angezeigt bzw. aufgelöst - Konflikte entstehen, wenn mehrere Benutzer z.B. dieselbe Zeile in einer Datei verändert haben Branches - (Entwürfe/Zweige) - Sind eine separate Kopie der aktuellen Dateien, die dann eigenständig verändert werden können - Wird genutzt, um z.B. ein neues Feature zu entwickeln ohne den Hauptbranch funktionsunfähig zu machen - Ist das Feature fertig, wird es wieder mit dem Hauptzweig vereinigt - Merge SOLID - Single responsibility principle - Jede Methode oder Klasse sollte nur einen Grund haben verändert zu werden - Open / Closed principle - Software (Methoden, Klassen, usw.) sollte für Erweiterungen offen und für Veränderungen geschlossen sein - Liskov Substitution principle - Objekte sollten durch ihre Superklasse ersetzt werden können, ohne die Applikation unbenutzbar zu machen - Interface Segregation principle - Clients sollten nicht von Interfaces abhängig sein, die sie nicht benötigen - Dependency Inversion principle - Code sollte nur von Abstraktionen abhängig sein, und nicht von konkreten Implementierungen. SCRUM