240 likes | 354 Views
Seminar aus Softwareentwicklung: Programmierstile. Robustheit. Christian Zeilinger ch.zeilinger@gmx.at. Übersicht. Einführung und Beispiele von Softwarefehler Was bedeutet „Defensives Programmieren“ Design by Contract Erfolgreiches Verwalten von Resourcen Self-Describing Data
E N D
Seminar aus Softwareentwicklung: Programmierstile Robustheit Christian Zeilinger ch.zeilinger@gmx.at
Übersicht • Einführung und Beispiele von Softwarefehler • Was bedeutet „Defensives Programmieren“ • Design by Contract • Erfolgreiches Verwalten von Resourcen • Self-Describing Data • Die Kunst des Testens Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 2/23
! ! Einführung Leider Tatsache: Es gibt keine perfekte Software! Also, was tun? Sich damit abfinden, oder gar verzweifeln? Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 3/23
SO NICHT!!! Die schlimmsten Softwarefehler • 1985: Software-Fehler in einem Röntgenapparat • 1996: Explosion der Ariane 5 am 4. Juni • 1999: Verglühen eines Mars Climate Orbiters • Rechtzeitig gefundener Fehler: F-16 T+36: Fehler im Inertial Reference System Hauptcomputer weist Triebwerke an, eine große Korrektur durchzuführen Rakete bricht aufgrund aerodynamischer Kräfte auseinander (Selbstzerstörung) Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 4/23
Defensives Programmieren Definition Robustheit: Fähigkeit von Softwaresystemen, auch unter außergewöhnlichen Bedingungen zu funktionieren Defensiv zu programmieren bedeutet, die Programme in Hinblick auf Robustheit zu gestalten Deshalb: Alle Annahme über Eingaben, Systemzustände usw. prüfen und bei Fehlern handeln (Assertions, Exceptions,…) Zum Beispiel: „Die Variable x muss hier positiv sein“ „Dieser Zeiger darf hier (eigentlich) nicht NULL sein“ Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 5/23
Design by Contract • Bedingungen, die gelten müssen, damit ein System funktionieren kann • Beispiel im realen Leben: Paketzustelldienst Bedingungen der Zustellfirma: • maximale Größe und Gewicht des Paketes • Bezahlung der Dienstleistung im Voraus Bedingungen von Seiten des Klienten: • Sorgfältiger Umgang mit der Fracht (keine Beschädigungen,…) • Ankunft des Gutes am gewünschten Zielort innerhalb einer gewissen Zeitspanne Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 6/23
Design by Contract • Preconditions Bedingungen, die gelten müssen, damit eine Methode (Komponente) ausgeführt werden kann • Postconditions Bedingungen, die nach dem Ausführen einer Methode gelten müssen (impliziert auch Resultate) • Invarianten Bedingungen, die aus Sicht des Rufers immer erfüllt sein müssen Beispiele: Class-Invarianten, Loop-Invarianten „Lazy Code“ Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 7/23
Design by Contract /* class invariant: * count enspricht der Anzahl der gesetzten Integer-Werte array ist ein Feld von Werten * die gesetzt werden können, wobei jeder noch nicht gesetzter Wert -1 entspricht. */ public class IntegerArray { ………………… public int Max { get { int max = -1; /* Loop-Invariante: * Vor jedem Durchlauf gilt: max = max(array[0:i-1]) (auch nach dem Ende der Schleife)*/ for(int i=0; i < array.Length; i++) if(array[i] > max) max = array[i]; return max; } } public int this[int index] { get { return array[index]; } // Precondition: 0 <= index < size set { array[index] = value; /* Nach der Ausführung dieser Anweisung ist die Klasseninvariante * verletzt, da count ungleich der der Anzahl der gesetzten Werte*/ count++; //Erst jetzt gilt die Klasseninvariante wieder } // Postcondition: Wert gesetzt } …………………… } Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 8/23
Design by Contract Überprüfung der Kontrakte • Mit Hilfe von Tools Tool für Java: iContract (im Kommentar: @pre, @post, @invariant) • Assertionen Methodenaufruf mit Übergabe der Bedingung - Beispiel: assert(x > 0); Bedingung erfüllt: okay Bedingung verletzt: Fehler -> Programmabbruch • Exceptions Sprachliche Unterstützung, um mit einfachen Mitteln: • dem Rufer einen Ausnahmefall mitzuteilen (throw) • ein (kollektives) Errorhandling zu bewerkstelligen (try …… catch) Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 9/23
Design by Contract Assertions using System.Diagnostics.Debug; public class IntegerArray { ………………… public int this[int index] { get { Debug.Assert(index >0 && index < array.Length);//Prüfung der Precondition return array[index]; } set { Debug.Assert(index >0 && index < array.Length);//Prüfung der Precondition array[index] = value; count++; } } public IntegerArray(int size) { Debug.Assert(size > 0);//Prüfung der Precondition array = new int[size]; for(int i=0; i<size; i++) array[i] = -1; } } Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 10/23
Design by Contract Exceptions #include <stdio.h> //without Exceptions int main() { int xMin, xMax, yMin, yMax, error = 0; FILE *file = fopen("file.cfg", "rb"); if (f != NULL) { error = 1; } else if(xMin = fgetc(file) == EOF) { error = 1; } else if(xMax = fgetc(file) == EOF) { error = 1; } else if(yMin = fgetc(file) == EOF) { error = 1; } else if(yMax = fgetc(file) == EOF) { error = 1; return -1; } if (error == 0) { printf("%d,%d,%d,%d", xMin,xMax,yMin,yMax); } else printf("Error reading file.cfg"); fclose(file); } using System; //using Exceptions using System.IO; class Test { static void Main() { int xMin, xMax, yMin, yMax; try { FileStream str = new FileStream("file.cfg", FileMode.Open); xMin = str.ReadByte(); xMax = str.ReadByte(); yMin = str.ReadByte(); yMax = str.ReadByte(); Console.WriteLine("{0},{1},{2},{3}", xMin,xMax,yMin,yMax); } catch(IOException) { Console.WriteLine("Error reading file.cfg"); } finally { //Code der in jedem Fall ausgeführt wird str.Close(); } } } Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 11/23
Resource-Balancing • Hauptspeicher, Threads, Dateien, Timer,… sind limitiert • Verhaltensmuster: allocate – use – deallocate using System.IO; class Budget { FileStream fileStr = null; void ReadBudget(string fileName, out int budget) { fileStr = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite); budget = (new BinaryReader(fileStr)).ReadInt32(); } void WriteBudget(int budget) { fileStr.Seek(0, SeekOrigin.Begin); (new BinaryWriter(fileStr)).Write(budget); fileStr.Close(); } public virtualint Update(string fileName, int newBudget) { int oldBudget; ReadBudget(fileName, out oldBudget); //Altes auslesen WriteBudget(newBudget); //Neues schreiben return newBudget; } } class NewBudget : Budget { public override int Update(string fileName, int newBudget) { int oldBudget; ReadBudget(fileName, out oldBudget); if (newBudget > 0) { //nur mit positiven neuem Budget überschreiben! WriteBudget(newBudget); return newBudget; } return oldBudget; } } Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 12/23
Resource-Balancing Finish What You Start! using System.IO; class Budget { protected void ReadBudget(FileStream fileStr, out int budget) { budget = (new BinaryReader(fileStr)).ReadInt32(); } protected void WriteBudget(FileStream fileStr, int budget) { fileStr.Seek(0, SeekOrigin.Begin); (new BinaryWriter(fileStr)).Write(budget); } public virtual int Update(string fileName, int newBudget) { int budget; FileStream fileStr = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite); ReadBudget(fileStr, out budget); //Altes Budget auslesen if (newBudget > 0) { WriteBudget(fileStr, newBudget); //eventuell mit neuem Budget überschreiben budget = newBudget; } fileStr.Close(); return budget; } } Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 13/23
Self-Describing Data Problem: Mysteriöse Daten! Linux-Verzeichnisausgabe mittels „dir“: total 44-rw-r--r-- 1 pr17 pr 4810 Dec 4 14:37 ETRC-rw-r--r-- 1 pr17 pr 4810 Dec 4 14:36 ETRC.BAKdrwxr-xr-x 2 root root 512 Oct 24 2001 TT_DB-rw-r--r-- 1 pr17 pr 0 Dec 4 17:02 out.txtdrwxr-xr-x 3 pr17 pr 512 Dec 4 14:19 rounddrwxr-xr-x 2 pr17 pr 512 Nov 7 15:12 testdrwxr-xr-x 2 pr17 pr 512 Dec 4 14:25 traces1drwxr-xr-x 2 pr17 pr 512 Dec 4 14:29 traces2drwxr-xr-x 2 pr17 pr 512 Dec 4 14:33 traces3drwxr-xr-x 2 pr17 pr 512 Nov 12 17:35 ueb13drwxr-xr-x 2 pr17 pr 512 Nov 20 16:51 ueb24drwxr-xr-x 2 pr17 pr 512 Dec 3 15:35 ueb32drwxr-xr-x 2 pr17 pr 512 Dec 3 17:43 ueb33drwxr-xr-x 2 pr17 pr 512 Dec 4 12:30 ueb42drwxr-xr-x 3 pr17 pr 512 Dec 4 12:17 ueb43 ??? Kundendaten: Hans Maier, 03/04/1976, 02/07/2001, 01/12/2002, 20 Christoph Huber, 01/02/1979, 01/01/2000, 11/02/2002, 10 Hannes Dorfer, 09/09/1958, 01/01/2000, 01/01/2002, 5 Linux-Prozessübersicht mittels: ps –ef | grep pr17 pr17 26763 1 0 17:00:58 ? 0:00 /system/apps/gup/bin/lamd -H 140.78.91.1 -P 59246 -n 0 -o 0pr17 26809 26739 0 17:04:16 pts/2 0:00 -tcshpr17 26802 26801 0 17:03:19 ? 0:00 /usr/local/bin/tcsh -c sftp-serverpr17 26739 26737 0 17:00:48 pts/2 0:00 -tcshpr17 26804 26802 0 17:03:19 ? 0:00 sftp-server Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 14/23
Self-Describing Data • Name-Value Pairs: Daten + Schema Beispiel: Kundendaten %name „Hans Maier“ %birthday „03/04/1976“ %firstTransaction „02/07/2001“ %discountStartDate „01/12/2002“ %discountPercent 20 %name „Christoph Huber“ ……… Speichereffizienz? Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 15/23
Self-Describing Data • Komprimierte Speicherung von Name-Value Pairs Beispiel: Kundendaten naHans Maier|bi03/04/1976|ft02/07/2001|ds01/12/2002|di20 naChristoph Huber| ......... Zusatzinformationen in einem Data Dictionary: ABBREVIATION NAME UNIT na name text bi birthday date ft firstTransaction date ds discountStartDate date di discount percent • Herkunft der Daten und Geschichte von Veränderungen Wer? Wann? Wieso? Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 16/23
Self-Describing Data • Name-Value Pairs in der Programmierung Bsp.:AddProduct(1001, „C# and .NET Reference“, 5, 29.90, 20, 10); Was bedeuten die einzelnen Werte? • Programmierdisziplin (Kommentare): AddProduct( 1001, //Produktnummer „C# and .NET Reference“, //Bezeichnung 5, //Stückzahl 29.90, //Verkaufspreis netto 20, //Mehrwertsteuer (in %) 10); //maximaler Rabatt • Sprachliche Unterstützung AddProduct(pNr = 1001, name = „C# and .NET Reference“, count = 5, price = 29.90, MWSt = 20, discount = 10); Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 17/23
Die Kunst des Testens • Fehler gefunden -> Test war erfolgreich • Ging alles gut -> Erfolgloser Test Testen bedeutet: Ein Programm mit der Absicht auszuführen, Fehler zu finden! Testen kann NICHT zeigen, dass ein Programm fehlerfrei ist. Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 18/23
Die Kunst des Testens Teste während der Codeerstellung • Testen im Software-Lebenszyklus • Test der Spezifikation • Modultest • Integrationstest • Systemtest • Abnahmetest • Systematisch Testen • inkrementelles Testen • beginnend mit den einfachen und grundlegenden Teilen • Welchen Output erwartet man sich? • Vergleich verschiedener Implementierungen • Coverage? Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 19/23
Die Kunst des Testens White-Box-Testing vs. Black-Box-Testing ? ? Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 20/23
Die Kunst des Testens • Abklären, was getestet werden soll z.B.: Methode: String ToUpperCase(String str, int startIndex, boolean unicode); • Wahl geeigneter Inputs • Äquivalenzklassen str: null, im ASCII-Code, im Unicode; unicode: true, false startIndex < 0, 0 <= startIndex < N, startIndex >=N • Randbereiche startIndex: -1, 0, 1, N-2, N-1, N; str: null, 1-Zeichen, 2-Z., N-Zeichen • Reduktion der Testeingaben „unmögliche“ Kombinationen: str.length == 0 und unicode == true nur für einen Parameter eine ungültige Äquivalenzklasse wählen • Festlegen der erwarteten Ausgabe • Durchführen des Test • Vergleich der Ausgabe mit der erwarteten Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 21/23
Die Kunst des Testens • Testautomation • Code-Review • Erzeugung von Eingabewerten • Generische Daten • Intelligente Daten • Stress-Tests • Regressionstesten • Testabbruch • wenn bestimmte Anzahl von Fehlern entdeckt wurde • 1 Fehler / 10-25 Anweisungen • Es bleiben immer Restfehler!!! • wenn bei gleichmäßiger Testanstrengung die Fehlerentdeckungsrate deutlich abnimmt Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 22/23
Zusammenfassung • Defensives Programmieren steigert die Robustheit von Softwareprodukten • Design by Contract trifft Aussagen über notwendige Bedingungen zur Ausführung von Software • Korrektes Resource-Balancing ist von hoher Notwendigkeit • Selbstbeschreibende Daten helfen bei der Analyse und tragen zum allgemeinen Verständnis bei • Ausreichende Tests sind der Schlüssel zu robusten Programmen Robustheit, Christian Zeilinger (ch.zeilinger@gmx.at) Folie 23/23