Design by Contract ‐ Modul SWE

Werbung

Nathan
Burgener
Design
by
Contract
‐
Modul
SWE
Nathan
Burgener
Inhaltsverzeichnis
1
Was
ist
Design
by
Contract......................................................................................................3
1.1
Überblick ................................................................................................................................3
1.2
Design
by
Contract
mit
Methoden ...............................................................................4
1.3
Design
by
Contract
mit
Klassen ....................................................................................5
1.4
Vererbung ..............................................................................................................................6
Design
by
Contract
in
Java...............................................................................................................7
1.5
Überblick ................................................................................................................................7
1.6
Assertions...............................................................................................................................7
1.6.1
Funktionsweise ...........................................................................................................7
1.6.2
Assertions
und
Design
by
Contract ....................................................................8
1.7
iContract .................................................................................................................................8
1.7.1
Einführung ....................................................................................................................8
1.7.2
Preconditions...............................................................................................................8
1.7.3
Postconditions .............................................................................................................9
1.7.4
Invarianten................................................................................................................. 10
1.7.5
Vererbung................................................................................................................... 11
1.7.6
Ausnahmebehandlung .......................................................................................... 11
1.8
Beispiele
für
bereits
bestehende
Methoden......................................................... 11
Zertifizierungsstelle
für
Komponenten .................................................................................. 13
Nathan
Burgener
1 Was
ist
Design
by
Contract
1.1 Überblick
Design
by
Contract
ist
ein
Konzept
aus
der
Softwaretechnik
von
Bertrand
Meyer.
Dabei
soll
die
Qualität
von
Programmmodulen
verbessert
werden,
indem
formale
Verträge
generiert
werden.
Bei
Design
by
Contract
soll
für
jede
Methode
eine
Art
Vertrag
erstellt
werden,
welcher
dann
vom
Aufrufer
aber
auch
von
der
Methode
selber
eingehalten
werden
muss.
Die
Verträge
existieren
in
zwei
Arten.
Als
Klasseninvarianten
und
als
Vor‐
und
Nachbedingungen
für
Funktionen
dieser
Klassen.
Da
das
Verfassen
eines
Vertrages
für
”grosse“
Funktionen
schwierig
wird,
ist
davon
auszugehen,
dass
Funktionen
von
den
meisten
Programmierern
auf
ein
Minimum
herabgebrochen
werden.
Das
dürfte
ein
Plus
an
Sicherheit
und
Lesbarkeit
des
gesamten
Codes
mit
sich
bringen.
Der
eigentliche
Sinn
von
Design
by
Contract
besteht
nun
darin,
genau
diese
Zusicherungen
zur
Laufzeit
auszuwerten.
Das
heisst,
dass
das
Programm
vor
und
nach
jedem
Methodenaufruf
kontrolliert
ob
der
Systemzustand
immer
noch
der
Spezifikation
entspricht.
Durch
das
Hinzufügen
von
solchen
Integritätsbedingungen
ist
der
Code
austauschbarer,
besser
lesbar,
einfacher
zusammenführbar
und
somit
langfristig
stabiler,
zuverlässiger
und
preiswerter.
Die
Vorteile
beim
Verwenden
von
Design
by
Contract
liegen
auf
der
Hand.
‐ Hilfe
beim
Schreiben
korrekter
Software
Um
die
Reliability
der
Software
zu
verbessern
sollte
man
Schnittstellen
sehr
genau
definieren.
Design
by
Contract
zwingt
den
Software
Engineer
dies
schon
früh
im
Design
Prozess
zu
tun.
Fehler
werden
so
früher
erkennt.
Der
Programmierer
wird
angeregt
sehr
genau
über
seinen
Code
nachzudenken.
So
bemerkt
er
auch
früher
die
Fehler
im
Programm
und
kann
reagieren
und
erhöht
so
auch
die
Wahrscheinlichkeit,
dass
das
Programm
den
Anforderungen
später
auch
gerecht
wird.
‐ Hilfe
bei
der
Dokumentation
Durch
die
Angabe
der
Pre‐
und
Postconditions
ist
schon
ein
Teil
der
Dokumentation
erledigt.
Die
Methode
ist
so
sauber
dokumentiert
und
es
ist
schön
ersichtlich,
was
für
Vorbedingungen
gelten
müssen
und
Nathan
Burgener
was
die
Methode
zurückgibt.
‐ Hilfe
beim
Testen,
Debuggen
und
bei
der
Qualitätssicherung
Durch
das
generieren
der
Verträge
werden
bereits
auch
Ansätze
für
das
Testen
erstellt.
Für
jeden
Vertrag
kann
auch
ein
Test
geschrieben
werden.
‐ Hilfe
beim
Erstellen
von
effektiven
Ausnahmebehandlungen
Basierend
auf
dem
Vertrag
können
die
Ausnahmebehandlungen
direkt
definiert
werden.
1.2 Design
by
Contract
mit
Methoden
Für
eine
Methode
wird
bei
Design
by
Contract
ein
Vertrag
erstellt,
welcher
beschreibt,
auf
welche
Art
und
Weise
auf
die
Methode
zugegriffen
wird.
Ebenfalls
werden
die
Auswirkungen
der
Methode
auf
den
Programmzustand
festgelegt.
Um
die
Software
zu
spezifizieren
und
Aussagen
über
ihre
Korrektheit
zu
machen
wird
das
Hoare‐Triple
verwendet.
Das
Hoare‐Triple
besteht
aus
3
Elementen:
Vorbedingung
P,
Nachbedingung
Q
und
Codesegment
S.
Das
Codesegment
entspricht
dabei
genau
einer
Methode
des
Programms.
Das
Hoare‐Tripel
sagt
aus,
dass
wenn
die
Methode
S
aufgerufen
wird,
während
P
gilt,
so
wird
nach
Ausführung
der
Methode
Q
gelten.
Dies
kann
folgendermassen
geschrieben
werden.
{P}
S
{Q}
Als
Beispiel
dafür
soll
eine
Divisionsmethode
doDivision
angeschaut
werden.
Diese
Methode
erhält
als
Übergabeparameter
den
Divisor
d.
doDivision(int
d)
{
globalResult
=
globalDivident
/
d;
}
Es
ist
klar
ersichtlich,
dass
der
Divisor
nicht
0
sein
darf.
Weiter
wird
erwartet,
dass
der
Divisor
nicht
negativ
ist.
Als
Programmierer
der
Methode
kann
man
jetzt
mit
if‐then
Anweisungen
mühsam
diese
Überprüfungen
machen.
Doch
man
weiss
nicht,
wie
dem
Benutzer
klar
zu
machen,
dass
seine
Eingabe
nicht
korrekt
war.
In
Java
geschieht
dies
normalerweise
mit
boolschen
Rückgabeparametern
oder
es
wird
eine
Exception
ausgelöst.
Um
dieses
Problem
mit
Hilfe
von
Design
by
Contract
zu
lösen,
wird
nun
ein
Vertrag
formuliert,
der
den
Aufrufer
verpflichtet
die
Methode
korrekt
zu
verwenden.
Als
Belohung
erhält
er
dafür
die
Garantie,
dass
das
Resultat
Nathan
Burgener
korrekt
berechnet
wird.
Wie
bereits
genannt
wird
dieser
Vertrag
mit
Hilfe
des
Hoare‐Tripels
formuliert:
{d
>
0}
doDivision
{globalResult
=
globalDivident
/
d}
Was
geschieht
aber
jetzt,
wenn
ein
Vertrag
verletzt
wurde?
Die
meisten
Programmiersprachen
werfen
dann
eine
Exception.
So
ist
schnell
ersichtlich,
wo
im
Programm
sich
der
Fehler
befindet.
Wird
eine
Vorbedingung
verletzt,
so
ist
der
Aufrufer
seine
Pflicht
einen
gültigen
Programmzustand
herzustellen
nicht
nachgekommen
und
das
Programm
kann
dem
Benutzer
dies
mitteilen,
damit
er
die
Eingabe
korrigiert.
Ist
hingegen
eine
Nachbedingung
verletzt
worden,
scheint
der
Programmierer
der
jeweiligen
Methode
seine
Arbeit
nicht
sorgfältig
erledigt
zu
haben.
1.3 Design
by
Contract
mit
Klassen
Die
Vor‐
und
Nachbedingungen
geben
an,
was
bei
den
Aufrufparameter
und
dem
Rückgabewert
der
Methode
erwartet
wird.
Zusätzlich
kann
man
jetzt
noch
Integritätsbedingungen
festlegen.
Die
sogenannten
Klasseninvarianten.
Klasseninvarianten
sind
Bedingungen,
die
für
die
gesamte
Klasse
und
nicht
nur
für
einzelne
Methoden
gelten
sollen.
Sie
werden
bei
jedem
Methodenaufruf
zu
Beginn
und
an
seinem
Ende
geprüft.
Die
Klasseninvariante
beschreibt
den
gültigen
Zustand
eines
Objektes
dieser
Klasse.
Als
gültigen
Zustand
meint
man
hier,
dass
das
Objekt
von
aussen
sinnvoll
verwendet
werden
darf.
Klasseninvarianten
dürfen
während
der
Nutzung
einer
Klasse
nicht
verletzt
werden.
Einzige
Ausnahme
davon
ist
der
Zeitraum
der
Ausführung
einer
Methode
einer
Klasse.
Hier
darf
eine
Klasseninvariante
verletzt
werden,
wenn
die
Funktion
sicherstellt,
dass
diese
nach
ihrer
Abarbeitung
wieder
gilt.
Als
Beispiel
gibt
es
hier
eine
WG‐Küche.
Jeder
darf
die
Küche
verwenden
und
auch
verschmutzen.
Er
muss
aber
sicherstellen,
dass
die
Küche
beim
Verlassen
wieder
tiptop
aufgeräumt
ist.
Als
Beispiel
in
Java
wird
eine
Klasse
Stack
genommen.
Die
Invariante
„items“
darf
hier
nicht
kleiner
als
0
sind
und
auch
nicht
grösser
als
die
Varaible
max.
public
class
Stack
{
private
int
items;
//
Anzahl
der
Elemente
/**
invariant
items>=0;
items<=max
**/
public
void
removeItem(){
}
Nathan
Burgener
//Element
löschen
1.4 Vererbung
Wird
eine
Unterklasse
erzeugt,
welche
von
der
Elternklasse
erbt,
so
werden
auch
die
Zusicherungen
vererbt.
Dabei
gelten
die
folgenden
Regeln:
Für
die
Invariante
gilt
das
gleiche
wie
für
die
Nachbedingung
‐ Die
in
der
Elternklasse
geltende
Invariante
bleibt
bestehen
und
wird
mit
der
Invariante
der
Unterklasse
weiter
verschärft.
Für
die
Vorbedingung
einer
Methode
‐ Die
Vorbedingung
kann
schwächer
werden,
da
ein
„oder“
mit
Vorbedingung
der
Elternklasse
und
der
neuen
Vorbedingung
gemacht
wird.
Für
die
Nachbedingung
einer
Methode
‐ Die
Vorbedingung
kann
stärker
werden,
da
ein
„und“
mit
Vorbedingung
der
Elternklasse
und
der
neuen
Vorbedingung
gemacht
wird.
Nathan
Burgener
Design
by
Contract
in
Java
1.5 Überblick
In
Java
wurden
während
den
Jahren
diverse
Tools
für
die
Implementation
von
Design
by
Contract
entwickelt.
So
gibt
es
Beispielsweise
iContract,
ContractJ,
Jass
und
weitere.
Einige
Tools
arbeiten
auch
mit
den
Assertions
von
Java.
Viele
Tools
wurden
jedoch
in
den
letzten
Jahren
nicht
mehr
weiter
entwickelt
und
es
gibt
auch
für
keines
der
Tolls
ein
Plugin
für
Netbeans.
Ersichtlich
ist
auch
noch,
dass
die
neueren
Tools
fast
alle
mit
Aspektorientierter Programmierung arbeiten.
1.6
Assertions
1.6.1 Funktionsweise
In
Java
1.4
wurden
Assertions
als
neues
Konzept
zur
Java‐
Sprachunterstütung
eingeführt.
Das
Assertions
Statement
überprüft
gewisse
Bedingungen
und
wird
entweder
true
oder
false.
Wird
beispielsweise
die
Prüfung
einer
Inputvariable
vergessen,
so
kann
es
unter
Umständen
passieren,
dass
das
Programm
vorerst
weiterläuft,
dann
aber
an
einem
späteren
Punkt
zur
Runtime
abstürzt.
Um
dies
zu
verhindern
kann
man
mit
einem
assert
Statement
früh
eine
gewünschte
Bedingung
prüfen.
Hier
ein
Beispiel
für
eine
solche
Überprüfung:
public
void
doDivision(int
d)
{
assert(d
>
0);
//Weiterführender
Code
}
Resultiert
ein
false
aus
der
Assertion,
so
bricht
das
Programm
mit
einem
java.lang.AssertionError
ab.
Assertions
werden
in
erster
Linie
zur
frühen
Fehlererkennung
verwendet.
Der
Vorteil
gegenüber
den
if‐Anweisungen
ist
die
Möglichkeit,
Assertions
zu
deaktivieren.
Bei
deaktivierten
Assertions
werden
die
assert
Anweisungen
nicht
ausgewertet
und
es
entsteht
keine
Effizienzeinbusse.
AssertionErrors
haben
etwa
die
gleiche
Bedeutung
wie
RuntimeExceptions.
Es
ist
wichtig
zu
unterscheiden
zwischen
den
unchecked
AssertionErrors
bzw.
RuntimeExceptions
und
den
checked
Nathan
Burgener
Exceptions,
die
deklariert
und
behandelt
werden
müssen.
Ein
Vorteil
von
Exceptions
gegenüber
Assertions
ist
die
Möglichkeit,
ein
Exception
Handling
zu
definieren
für
abnormale
Fälle,
von
denen
man
weiss,
dass
sie
eintreten
und
behandelt
werden
können.
1.6.2 Assertions
und
Design
by
Contract
Wie
in
der
Beschreibung
der
Assertions
ersichtlich
ist,
werden
Vor‐
und
Nachbedingungen
sowie
Klasseninvarianten
nicht
unterstützt.
Assertions
kann
also
nicht
wirklich
für
Design
by
Contract
verwendet
werden.
Assertions
haben
immer
die
gleiche
Semantik.
Die
Information
ob
es
sich
um
pre‐
oder
postconditions
handelt,
lässt
sich
nur
anhand
des
zugehörigen
Kommentars
erkennen
und
sind
deshalb
weniger
formell.
Weiter
sind
Assertions
nicht
frei
von
Seiteneffekten.
Dadurch
können
sich
Fehler
einschleichen,
die
ohne
sie
nicht
möglich
gewesen
wären
und
die
schwer
aufzufinden
sind.
Assertions
lassen
sich
nicht
vererben.
Somit
muss
in
der
Unterklasse
auch
alles
noch
einmal
implementiert
werden,
was
eine
Duplizierung
von
Code
bedeutet.
1.7
iContract
1.7.1 Einführung
Die
Implementierung
der
Zusicherungen
in
Java
erfolgt
in
den
Interfaces
der
Klassen.
Sie
stehen
jeweils
als
Kommentare
vor
und
nach
der
Methode,
so
dass
sie
nicht
mit
dem
Rest
des
Quellcodes
vermischt
werden.
Es
gibt
also
ein
weiteres
File,
in
dem
alle
Verträge
definiert
sind.
Die
Zusicherungen
wir
mittels
folgenden
Abkürzungen
realisiert:
‐ Precondition:
@pre
‐ Postcondition:
@post
‐ Invarianten:
@invariant
iContract
ist
ein
Präprozessor
für
Java.
Um
dieses
Tool
zu
verwenden
wird
der
Code
zuerst
durch
den
iContract
gelassen.
Dieser
erzeugt
aus
den
Kommentaranweisungen
Assertion‐Checks
und
fügt
diese
in
den
Source
Code
ein.
1.7.2 Preconditions
In
iContract
werden
die
Preconditions
im
Header
der
Methode
mit
der
Direktive
@pre
beschrieben.
Hier
ein
Beispiel:
Nathan
Burgener
/**
* @pre f >= 0.0
*/
public float wurzel(float f) { ... }
In diesem Beispiel muss der Parameter f der Funktion wurzel() grösser
oder gleich Null sein.
1.7.3 Postconditions
Postconditions werden auch die die Preconditions im Header der
jeweiligen Methode definiert. Dafür wird die Direktive @post
verwendet. Hier ein Beiepiel dafür
/**
* @pre f >= 0.0
* @post Math.abs((return * return) - f) < 0.001
*/
public float wurzel(float f) { ... }
Diese Postcondition besagt, dass die Methode die Wurzel der Zahl f
berechnet und dies mit einer maximalen Abweichung von 0.001.
iContract verwendet einige spezielle Notationen für die Postconditions.
Beispielsweise steht das return für den Rückgabewert der Methode. Es
wird also zur Laufzeit durch den Rückgabewert der Methode ersetzt.
In einer Postcondition muss oftmals auf einen Wert zugegriffen, der
vor und nach der Ausführung der Methode nicht gleich ist. Um dies zu
unterscheiden, kann mit @pre in der Postcondition auf den Wert vor
dem Ausführen der Methode zugegriffen werden. Hier ein Beispiel
dazu:
Nathan
Burgener
/**
* Fügt ein Element zu einer Collection hinzu
*
* @post c.size() = [email protected]() + 1
* @post c.contains(o)
*/
public void hinzu(Collection c, Object o) { ... }
Hier wird mit [email protected]() auf die Grösse der Collection vor dem
Ausführen der Methode zugegriffen.
1.7.4 Invarianten
Die Invarianten werden mit iContract im Header der Klasse definiert.
Hier ein Beispiel dafür:
/**
* A PositiveInteger ist ein Integer, der garantiert positiv ist.
*
* @inv intValue() > 0
*/
class PositiveInteger extends Integer
{
//Code der Klasse
}
Die Invariante garantiert hier, dass der PositivsIntegers’s Wert immer
grösser als Null. Dieses Assertions wird immer vor und nach der
Ausführung der Methoden dieser Klasse geprüft.
Nathan
Burgener
1.7.5 Vererbung
iContract unterstützt auch die Vererbung. Das heisst alle Invarianten,
Preconditions und Postconditions die in der Super-Klasse definiert
werden, müssen auch von der Unterklasse eingehalten werden. In den
Unterklassen können natürlich noch weitere hinzugefüt werden. Das
Konzept funktioniert auch mit Interfaces. Wenn es 2 Interfaces gibt, in
welchen Invarianten, Preconditions und Postconditions definiert
werden und eine Klasse diese beide implementiert, sind diese auch
auch für die Klasse gültig.
1.7.6 Ausnahmebehandlung
Das Tool iContract ermöglicht bei einer Verletzung der Zusicherung,
die eine Ausnahme(Exception) wirft, diese gleich abzufangen. So muss
das Programm nicht abgebrochen werden, wenn eine Ausnahme
voraussehbar ist. Hier ein Beispiel dafür:
/*
*@pre i>= 0 #ArrayIndexOutOfBoundsException
*/
1.8
Beispiele
für
bereits
bestehende
Methoden
/**
* Creates a new user for the application
* @param vorname String, which represents the users first name
* @param nachname String, which represents the users last name
* @param benutzername String, which represents the user name
* @param passwort String, which represents the password
*/
public void newReg(String vorname, String nachname, String
benutzername, String passwort) {
EntityManager em = emf.createEntityManager();
EntityTransaction utx = em.getTransaction();
NewUser nUser = new NewUser();
nUser.setVorname(vorname);
nUser.setNachname(nachname);
nUser.setBenutzername(benutzername);
Nathan
Burgener
nUser.setPasswort(passwort);
try {
utx.begin();
em.persist(nUser);
utx.commit();
} catch(Exception ex){
try {
utx.rollback();
} catch (Exception e) {}
throw new RuntimeException("Error creating entity", ex);
} finally {
em.close();
}
}
Preconditions
für
diese
Methode:
• vorname
darf
nicht
Null
sein
• nachname
darf
nicht
Null
sein
• benutzername
darf
nicht
Null
sein
• passwort
darf
nicht
Null
sein
• passwort
muss
mehr
als
10
Zeichen
beinhalten
• emf.createEntityManager()
darf
nicht
Null
sein
• em.getTransaction()
darf
nicht
Null
sein
Postconditions
für
diese
Methode:
• DB
erhält
neues
User
Object
• em.isOpen()
ist
false
Invariante
für
die
Klasse:
• Der
EntityManagerFactory
emf
darf
nie
null
sein.
Nathan
Burgener
Zertifizierungsstelle
für
Komponenten
An
der
ETH
wurde
1998
mit
einem
Projekt
mit
dem
Namen
Trusted
Components
gestartet.
Das
ganze
verwendet
das
Konzept
von
Design
by
Contract.
Dafür
muss
eine
detaillierte
Beschreibung
gemacht
werden
und
die
Qualität
der
Software
garantiert
werden.
Zur
Qualität
gehören
Punkte
wie
Sicherheit,
Robustheit,
Korrektheit,
Performance
usw.
Der
grosse
Vorteil
von
Trusted
Components
liegt
dabei
in
der
Wiederverwendung.
Die
zertifizierten
Komponenten
können
jetzt
einfach
wiederverwendet
werden.
So
wird
viel
Zeit
beim
Implementieren
von
neuen
Programmen
gespart.
Das
Projekt
wurde
aber
nie
richtig
fertiggestellt.
Der
Grund
dafür
ist
nicht
bekannt.
Ich
denke
aber,
dass
so
etwas
nur
sehr
schwer
umsetzbar
ist.
Zuerst
müsste
ein
allgemeines
Framework
definiert
und
erstellt
werden,
damit
alle
mit
dem
gleichen
arbeiten
und
dann
müsste
dieses
Framework
auch
von
den
Entwicklungsumgebungen
unterstützt
werden.
Das
schwierige
ist
jetzt
wohl
die
Punkte
Sicherheit,
Robustheit,
Korrektheit,
Performance
usw.
auch
zu
garantieren.
Nathan
Burgener
Quellen
http://www.wikiservice.at/dse/wiki.cgi?DesignByContract
http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html
http://www.javaworld.com/javaworld/jw-02-2001/jw-0216cooltools.html?page=4
http://archive.eiffel.com/doc/manuals/technology/contract/
www.gruntz.ch/courses/sem/ws06/DBC.pdf
http://swt.cs.tuberlin.de/lehre/mwsp/ws0607/ausarbeitungen/Ausar
beitung-6.pdf
www.metafinanz.de/fileadmin/Dokumente/Leistungen/1_Download/2
007_hsr_eclipse.pdf
http://seal.ifi.uzh.ch/fileadmin/User_Filemount/Vorlesungs_Folien/Se
minar_SE/SS06/kandrical_design_contract.pdf
http://www2.hsaugsburg.de/informatik/projekte/testen/ss2006/builtI
nTest.pdf
Herunterladen