Apr 06, 2015
Seminar aus Softwareentwicklung: Programmierstile
Christian [email protected]
Ü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 ([email protected]) Folie 2/23
Einführung
Es gibt keine perfekte Software!! !
Leider Tatsache:
Also, was tun?
Sich damit abfinden, oder gar verzweifeln?
Robustheit, Christian Zeilinger ([email protected]) Folie 3/23
Robustheit, Christian Zeilinger ([email protected]) Folie 4/23
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)
SO NICHT!!!
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 ([email protected]) Folie 5/23
Design by Contract
• Bedingungen, die gelten müssen, damit ein System funktionieren kann
• Beispiel im realen Leben: PaketzustelldienstBedingungen 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 ([email protected]) Folie 6/23
Design by Contract
• PreconditionsBedingungen, die gelten müssen, damit eine Methode (Komponente) ausgeführt werden kann
• PostconditionsBedingungen, die nach dem Ausführen einer Methode gelten müssen (impliziert auch Resultate)
• InvariantenBedingungen, die aus Sicht des Rufers immer erfüllt sein müssen
Beispiele: Class-Invarianten, Loop-Invarianten
„Lazy Code“
Robustheit, Christian Zeilinger ([email protected]) 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 < sizeset {
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 ([email protected]) Folie 8/23
Design by Contract
Überprüfung der Kontrakte
• Mit Hilfe von ToolsTool für Java: iContract (im Kommentar: @pre, @post, @invariant)
• AssertionenMethodenaufruf mit Übergabe der Bedingung - Beispiel: assert(x > 0);
Bedingung erfüllt: okay
Bedingung verletzt: Fehler -> Programmabbruch
• ExceptionsSprachliche Unterstützung, um mit einfachen Mitteln:
- dem Rufer einen Ausnahmefall mitzuteilen (throw)- ein (kollektives) Errorhandling zu bewerkstelligen (try …… catch)
Robustheit, Christian Zeilinger ([email protected]) Folie 9/23
Design by Contract
using System.Diagnostics.Debug;
public class IntegerArray {…………………public int this[int index] {
get { Debug.Assert(index >0 && index < array.Length); //Prüfung der Preconditionreturn array[index];
}
set {Debug.Assert(index >0 && index < array.Length); //Prüfung der Preconditionarray[index] = value;count++;
}}
public IntegerArray(int size) {Debug.Assert(size > 0); //Prüfung der Preconditionarray = new int[size]; for(int i=0; i<size; i++) array[i] = -1;
}}
Assertions
Robustheit, Christian Zeilinger ([email protected]) Folie 10/23
Design by ContractExceptions
#include <stdio.h> //without Exceptionsint 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 wirdstr.Close();
}}
}Robustheit, Christian Zeilinger ([email protected]) Folie 11/23
Resource-Balancing
• Hauptspeicher, Threads, Dateien, Timer,… sind limitiertVerhaltensmuster: 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 virtual int Update(string fileName, int newBudget) {
int oldBudget;ReadBudget(fileName, out oldBudget); //Altes auslesenWriteBudget(newBudget); //Neues schreibenreturn 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 ([email protected]) 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 auslesenif (newBudget > 0) {
WriteBudget(fileStr, newBudget); //eventuell mit neuem Budget überschreibenbudget = newBudget;
}fileStr.Close();return budget;
} }
Robustheit, Christian Zeilinger ([email protected]) Folie 13/23
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
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
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 ([email protected]) Folie 14/23
Self-Describing Data
• Name-Value Pairs: Daten + SchemaBeispiel: 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 ([email protected]) Folie 15/23
Self-Describing Data
• Komprimierte Speicherung von Name-Value PairsBeispiel: 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änderungenWer? Wann? Wieso?
Robustheit, Christian Zeilinger ([email protected]) Folie 16/23
Self-Describing Data
• Name-Value Pairs in der ProgrammierungBsp.: 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ützungAddProduct(pNr = 1001, name = „C# and .NET Reference“, count = 5, price = 29.90,
MWSt = 20, discount = 10);
Robustheit, Christian Zeilinger ([email protected]) 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 ([email protected]) Folie 18/23
Die Kunst des Testens
• 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?
Teste während der Codeerstellung
Robustheit, Christian Zeilinger ([email protected]) Folie 19/23
Die Kunst des Testens
White-Box-Testing
vs.
Black-Box-Testing
?
?
Robustheit, Christian Zeilinger ([email protected]) 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, falsestartIndex < 0, 0 <= startIndex < N, startIndex >=N
– RandbereichestartIndex: -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 == truenur 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 ([email protected]) 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 ([email protected]) Folie 22/23
Zusammenfassung
Robustheit, Christian Zeilinger ([email protected]) Folie 23/23
• 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