Agent and Object Technology Lab Dipartimento di Ingegneria dell’Informazione
Università degli Studi di Parma
AOT AOT LAB LAB
Ingegneria del software A
Sviluppo basato su contratti (contratti, eccezioni, asserzioni)
Michele Tomaiuolo
Agent and Object Technology Lab Dipartimento di Ingegneria dell’Informazione
Università degli Studi di Parma
AOT AOT LAB LAB
Contratti - Sommario
Specifica dei requisiti
Pre- e post-condizioni, invarianti
Responsabilità e contratti
Linguaggi
3
AOT AOT
LAB LAB Correttezza
Rispetto a cosa valutiamo la correttezza di un programma?
Idea del programmatore
Non formulata, non documentata, facilmente dimenticata
A volte incompleta, mutevole
Specifiche
Formulate, scritte
Studiate attentamente e condivise
Formali o informali
Parte del programma
AOT AOT
LAB LAB Specifiche formali
Tecniche formali per la specifica di tipi di dati astratti 1. Specifiche algebriche
Equazioni che definiscono come i metodi operano sui dati
2. Specifiche assiomatiche
Espressioni logiche (asserzioni) associate a classi e metodi
Spesso distinte in invarianti, precondizioni e postcondizioni
5
AOT AOT
LAB LAB Precondizioni and postcondizioni
Precondizioni
Stabiliscono se è possibile chiamare un metodo
Prerequisiti per l’attivazione
Postcondizioni
Stabiliscono se il metodo restituisce il valore atteso, o ha l’effetto desiderato
… In relazione ai parametri (che soddisfano le precondizioni)
Definiscono il significato del metodo
AOT AOT
LAB LAB Linguaggio per asserzioni
Espressioni logiche
Espressioni e funzioni booleane del linguaggio di programmazione
Semplici, ma problema se errori al loro interno
Quantificatori
Universali (per ogni…) ed esistenziali (esiste…)
Richiedono programmazione, cicli ecc.
Possono essere dispendiosi da valutare
Strumenti speciali
old expr – Il valore dell’espressione all’inizio del metodo
nochange – Modo per imporre che il metodo non cambi lo stato dell’oggetto
Altrimenti asserzioni informali
Scritte in linguaggio naturale
7
AOT AOT
LAB LAB Divisione delle responsabilità
Una classe gestisce certe funzionalità, da esprimenre come responsabilità a cui non dovrebbe venir meno
Dividere le responsabilità ed evitare sovrapposizioni
Senza divisione ben definita delle responsabilità
Tutte le classi assumono grosse responsabilità
Tutte le parti del programma controllano tutte le possibili condizioni (programmazione difensiva)
Un grosso programma viene ancora più grosso
Con divisione ben definita delle responsabilità
Metodi possono operare con sicurezza sotto certe assunzioni
È chiaro quali parti dovrebbero controllare certe condizioni
Simplifica il programma
AOT AOT
LAB LAB Oggetti e loro responsabilità
Pre- e post-condizioni usate per dividere le
responsabilità tra classi in programmi object-oriented
Soddisfare le precondizioni è responsabilità dell’oggetto chiamante, o client
Soddisfare le postcondizioni è responsabilità dell’oggetto chiamato, o server
Errore del chiamante se una precondizione di un metodo non è soddisfatta
Errore del metodo stesso se una delle sue postcondizioni non è soddisfatta
9
AOT AOT
LAB LAB Responsabilità e contratti
Precondizioni + postcondizioni = contratto
… tra classe chiamante e classe chiamata
L’infrazione di un contratto dovrebbe essere trattata come problema serio
Un errore rispetto alle specifiche
Dovrebbe sollevare una eccezione
Con tutta probabilità interrompere l’esecuzione
AOT AOT
LAB LAB Esempio di contratto
sqrt(x : float) : float
Precondizioni: x >= 0
Postcondizioni: abs(result * result - x) <= 0.000001
Oggetto chiamante
Obblighi: deve passare un numero non negativo
Benefici: riceve la radice del numero
Oggetto chiamato
Obblighi: restituisce un numero r tale che r * r = x
Benefici: può assumere che x non è negativo
11
AOT AOT
LAB LAB Invariante di classe
Invariante di classe: vincolo che deve valere per ogni stato stabile di un oggetto, durante tutto il suo ciclo di vita
Rafforzamento generale di pre- e post-condizioni
“Criterio di sanità” dell’oggetto
Deve essere soddisfatto dal costruttore
Deve essere mantenuto dai metodi pubblici
Ma non necessariamente da metodi privati o protetti
AOT AOT
LAB LAB Ereditarietà e contratti
Classe A { op(. . .) {
require pre-cond-1 . . .
ensure post-cond-1 }
invariant inv-cond-1 }
Classe B : A { op(. . .) {
require pre-cond-2 . . .
ensure post-cond-2 }
invariant inv-cond-2 }
Che relazione c’è tra le asserzioni di una classe e quelle dei suoi discendenti?
?
?
?
13
AOT AOT
LAB LAB Ereditarietà e contratti
Polimorfismo: possibile invocazione del metodo di una sottoclasse, anzichè quello della superclasse
I metodi delle sottoclassi non possono reidefinire arbitrariamente i metodi delle superclassi
I contratti della sottoclasse devono rispettare i contratti della superclasse, devono essere loro sottocontratti
1. Le precondizioni non devono essere più forti
2. Le postcondizioni non devono essere più deboli
3. Gli invarianti di classe non devono essere più deboli
AOT AOT
LAB LAB Invarianti di ciclo
Invarianti non limitati a classi
Invarianti di ciclo importante strumento di controllo
int div(int x, int y) { // division by repeated subtraction // require x >= 0 && y > 0;
int rest = x;
int quotient = 0;
while (rest >= y) {
// invariant rest + quotient * y = x;
rest = rest - y;
quotient = quotient + 1;
}
return quotient;
// ensure rest + quotient * y = x && rest < y && rest >= 0;
15
AOT AOT
LAB LAB Design by contract
Design by Contract (DBC): uso di asserzioni in varie vasi di sviluppo
Progetto: approccio pragmatico alle specifiche
Implementazione: guida e limita la programmazione
Documentazione: importanti informazioni aggiunte alle interfacce
Test: controllo a run time per rispetto delle specifiche
• Le precondizioni limitano il lavoro di test
• Le postcondizioni e gli invarianti di classe aiutano a trovare errori
Uso finale: sollevate eccezioni se le asserzioni sono violate
AOT AOT
LAB LAB Linguaggi e contratti
Eiffel è il linguaggio che supporta meglio il paradigma Design by Contract
Paradigma proposto e sviluppato da Betrand Meyer (creatore del linguaggio Eiffel)
Implementazioni per Java disponibili come estensioni
Spesso sfruttano il meccanismo delle annotazioni
JContract, JContractor, Contract4J, C4J, DBCProxy…
Implementazioni per C# e altri linguaggi .NET
Molti altri…
Agent and Object Technology Lab Dipartimento di Ingegneria dell’Informazione
Università degli Studi di Parma
AOT AOT LAB LAB
Eccezioni - Sommario
Stack delle chiamate
Tipi di eccezione
Gestione delle eccezioni
Lancio di eccezioni
AOT AOT
LAB LAB Cos’è un’eccezione?
Il termine eccezione è un’abbreviazione per “evento eccezionale”
Evento che si verifica durante l’esecuzione di un
programma e rompe il suo normale flusso di istruzioni
Quando si verifica un errore in un metodo, il metodo lancia (throw) un’eccezione
Il metodo crea un oggetto eccezione con informationi sull’errore, il suo tipo e le condizioni in cui si è verificato
… e lo consegna all’ambiente di runtime
La piattaforma cerca qualcosa che gestisca l’eccezione
19
AOT AOT
LAB LAB Stack delle chiamate
Call stack
Lista ordinata di metodi invocati al momento
dell’errore
Exception handler
La piattaforma cerca nel call stack un metodo che contenga un blocco di codice che possa gestire l’eccezione
AOT AOT
LAB LAB Ricerca del gestore di eccezioni
La ricerca comincia nel metodo dove si verifica l’errore, e prosegue in ordine inverso rispetto alle invocazioni
1. Se c’è un gestore
appropriato per il tipo di eccezione…
Il gestore cattura (catch) l’eccezione
2. Se non c’è un gestore appropriato…
21
AOT AOT
LAB LAB Catch or specify
I programmi Java, per essere compilati, devono rispettare la regola:
“Gestisci o specifica le eccezioni”
Codice che può lanciare una eccezione racchiuso in:
1. Un blocco try che catturi l’eccezione (handler) 2. Un metodo che specifichi il lancio dell’eccezione:
clausola throws che includa l’eccezione
La regola non vale per tutte le eccezioni
Tre categorie fondamentali di eccezioni
Solo una soggetta alla regola
AOT AOT
LAB LAB Gerarchia di eccezioni
23
AOT AOT
LAB LAB Eccezioni controllate
Condizioni eccezionali che una applicazione ben scritta (affidabile) dovrebbe prevedere e risolvere
Es. Richiesto nome file per costruire un FileReader
Ma file non esistente
Il costruttore lancia FileNotFoundException
Un programma ben scritto cattura l’eccezione, notifica all’utente l’errore e chiede un nuovo nome di file
Le eccezioni controllate (checked) sono soggette alla regola “cattura o specifica”
Tutte le eccezioni sono controllate
Eccetto Error, RuntimeException, e sottoclassi
AOT AOT
LAB LAB Errori
Condizioni eccezionali esterne all’applicazione, che l’applicazione spesso non può prevedere o risolvere
Non soggetti alla regola “cattura o specifica”
Error e sottoclassi
Es. Si apre un file in lettura
Ma c’è un guasto hardware o di sistema che impedisce la lettura
Viene lanciato IOError
L’applicazione potrebbe catturare l’eccezione, ma potrebbe anche andar bene terminare l’esecuzione (con messaggio o stack trace)
25
AOT AOT
LAB LAB Eccezioni di runtime
Condizioni eccezionali interne all’applicazione, ma che non si riesce a prevedere o risolvere (spesso bug)
Non soggette alla regola “cattura o specifica”
Bug o uso scorretto di API
RuntimeException e sottoclassi
Es. Nome file per creare FileReader
A causa di un errore logico si passa null al costruttore…
Che lancia NullPointerException
L’applicazione può catturare l’eccezione, ma probabilmente…
Ha più senso eliminare il bug che provoca l’accezione!
Errori ed eccezioni di runtime sono noti assieme come eccezioni non controllate (unchecked)
AOT AOT
LAB LAB ListOfNumbers
public class ListOfNumbers { // This class won't compile by design!
private List vector;
private static final int SIZE = 10;
public ListOfNumbers () {
vector = new ArrayList(SIZE);
for (int i = 0; i < SIZE; i++) { list.add(i); // autoboxing
} }
public void writeList() {
PrintWriter out = new PrintWriter(new FileWriter("out.txt"));
for (int i = 0; i < SIZE; i++) {
out.println("Value at: " + i + " = " + vector.get(i));
}
out.close();
} }
27
AOT AOT
LAB LAB Errori di compilazione
Costruttore FileWriter
Apre un file testuale in scrittura
Lancia IOException
Se il file non può essere aperto
Errore di compilazione (checked)
Metodo List.get
Lancia ArrayIndexOutOfBoundsException
Se l’indice è fuori range
index < 0 || index >= size()
Ma non genera un errore a tempo di compilazione (unchecked)
AOT AOT
LAB LAB Blocco try
1. Blocco try per ogni istruzione che genera eccezione 2. Blocco try unico che cattura tipi diversi di eccezioni
try {
System.out.println("Entered try statement");
out = new PrintWriter(new FileWriter("OutFile.txt"));
for (int i = 0; i < SIZE; i++) {
out.println("Value at: " + i + " = "
+ vector.get(i));
} }
29
AOT AOT
LAB LAB Blocchi catch
Ogni blocco catch è un gestore d’eccezioni…
Per il tipo d’eccezione indicato come argomento
La piattaforma invoca il primo gestore nello stack…
Il cui tipo di argomento generalizza il tipo dell’eccezione lanciata
try { // …
} catch (FileNotFoundException e) {
System.err.println("FileNotFoundException: " + e.getMessage());
} catch (IOException e) {
System.err.println("Caught IOException: " + e.getMessage());
}
AOT AOT
LAB LAB Gestire una eccezione
Il gestore può fare più che scrivere messaggi
Error recovery
Chiedere all’utente di prendere una decisione
Propagare l’errore all’esterno usando eccezioni concatenate…
Possibile visualizzare lo stack dei metodi
Utile in fase di debugging
Throwable.printStackTrace
31
AOT AOT
LAB LAB Blocco finally
Eseguito sempre
Impedisce che le istruzioni di cleanup siano scavalcate
Es. Il file dovrebbe venir chiuso in tutti e tre i casi
IOException, ArrayIndex…, nessuna eccezione
try { // …
} catch { // … } finally {
if (out != null) {
System.out.println("Closing PrintWriter");
out.close();
} else {
System.out.println("PrintWriter not open");
} }
AOT AOT
LAB LAB Try, catch, finally
public void writeList() { PrintWriter out = null;
try {
System.out.println("Entering try statement");
out = new PrintWriter(new FileWriter("out.txt"));
for (int i = 0; i < SIZE; i++) {
out.println("Value at: " + i + " = " + vector.get(i));
}
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println("Wrong array index: " + e.getMessage());
} catch (IOException e) {
System.err.println("Caught IOException: " + e.getMessage());
} finally {
if (out != null) {
System.out.println("Closing PrintWriter");
out.close();
} else {
System.out.println("PrintWriter not open");
}
33
AOT AOT
LAB LAB Possibili esecuzioni
1. new FileWriter lancia IOException
2. vector.get lancia ArrayIndexOutOfBounds…
3. out.println lancia IOException 4. Nessuna eccezione
(Seguire il flusso di istruzione nei diversi casi)
AOT AOT
LAB LAB Altrimenti: throws
Se un metodo non cattura le eccezioni controllate che possono verificarsi al suo interno…
Deve specificare che può lanciarle
public void writeList() throws IOException, ArrayIndexOutOfBoundsException { . . . }
Per eccezioni non controllate non è obbliatorio
public void writeList() throws IOException { . . . }
35
AOT AOT
LAB LAB Lanciare eccezioni
Una eccezione può essere lanciata da
Codice utente, librerie esterne, librerie Java, piattaforma
Sempre lanciata con l’istruzione throw
Disponibili in Java numerose classi di eccezioni
Tutte discendenti di Throwable
Possibile crearne di nuove, per diversi contesti applicativi
public Object pop() { // in a typical Stack class Object obj;
if (size == 0) {
throw new EmptyStackException(); // unchecked }
obj = get(size - 1); set(size - 1, null); size--;
return obj;
}
AOT AOT
LAB LAB Eccezioni concatenate
Una applicazione può rispondere ad una eccezione lanciandone un’altra
Si dice che la prima eccezione causa la seconda
Metodi e costruttori utili
Throwable getCause()
Throwable initCause(Throwable)
Throwable(String, Throwable)
Throwable(Throwable)
try { // …
} catch (IOException e) {
37
AOT AOT
LAB LAB Scegliere le eccezioni
Lanciare un’eccezione, o restituire un valore?
1. Situazione realmente fuori dell’ordinario?
2. Possibile per il chiamate ignorare il problema?
3. Oggetto in stato inconsistente?
Creare un nuovo tipo di eccezione, o riutilizzare uno di quelli esistenti?
1. Errore concettualmente simile ad una eccezione esistente?
2. Eccezione esistente, controllata o no, di tipo soddisfacente?
3. Nuova eccezione riconducibile ad un tipo di base, da estendere?
AOT AOT
LAB LAB Aggirare la gestione delle eccezioni
Java non impone ai metodi di catturare o specificare le eccezioni non controllate (RuntimeException, Error e sottoclassi)
I programmatori possono essere tentati di scrivere codice che lancia solo eccezioni non controllate
Scrivere tutte le eccezioni come sottoclassi di RuntimeException
Permettono di scrivere codice senza curarsi di errori di compilazione e senza curarsi di specificare o catturare eccezioni
Può sembrare conveniente, ma si perdono tutti i vantaggi del controllo delle eccezioni, legati alla divisione delle
39
AOT AOT
LAB LAB Perché specificare le eccezoni?
Perchè i progettisti Java hanno imposto ai metodi di
specificare tutte le eccezioni lanciate al suo interno e non catturate?
Eccezioni lanciate da un metodo sono parte della loro interfaccia pubblica
Chi chiama un metodo deve sapere quali sono le eccezioni lanciate per poter decidere cosa farne
Queste eccezioni sono parte dell’interfaccia pubblica di un metodo al pari dei parametri e del valore di ritorno
AOT AOT
LAB LAB E perché alcune non specificate??
Se è bene documentare un metodo, includendo le sue eccezioni… allora perchè non specificare anche le
eccezioni di runtime?
Risultato di problemi di programmazione, bug
Al codice client non è richiesto di risolverli o gestirli
Eccezioni aritmetiche, come divisione per zero
Eccezioni sui riferimenti, come accesso a metodi su riferimenti a null
Eccezioni sugli indici di array e liste
Il compilatore non richiede che esse siano catturate o specificate (anche se si può)
Molto numerose e diffuse
Altrimenti programmi meno chiari
41
AOT AOT
LAB LAB Linee guida
Esempio tipico: se uno dei parametri richiesti è null, il metodo lancia NullPointerException
La chiamata al metodo è errata, bisogna correggere il bug
Non lanciare una eccezione di runtime o creare
sottoclassi di RuntimeException solo per evitare di doverle specificare
Se un client può ragionevolmente risolvere l’eccezione, allora dovrebbe essere controllata
Se un client non può far nulla per risolverla, allora dovrebbe essere non controllata
AOT AOT
LAB LAB Vantaggi
Gestione errori separata dal codice “regolare”
Propagazione errori attraverso lo stack delle chiamate
Classificazione dei tipi d’errore
43
AOT AOT
LAB LAB Gestione errori separata
errorCodeType readFile { initialize errorCode = 0;
open the file;
if (theFileIsOpen) {
determine file length;
if (gotTheFileLength) { allocate memory;
if (gotEnoughMemory) { read file into mem;
if (readFailed) { errorCode = -1;
}
} else { errorCode = -2; } } else { errorCode = -3; } close the file;
if (theFileDidntClose &&
errorCode == 0) { errorCode = -4;
} else {
errorCode = errorCode and -4;
}
} else { errorCode = -5; } return errorCode;
}
readFile { try {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
} catch (fileOpenFailed) { doSomething;
} catch (sizeDeterminationFailed) { doSomething;
} catch (memoryAllocationFailed) { doSomething;
} catch (readFailed) { doSomething;
} catch (fileCloseFailed) { doSomething;
} }
AOT AOT
LAB LAB Propagazione errori
method1 {
errorCodeType error;
error = call method2;
if (error) doErrorProcessing;
else proceed;
}
errorCodeType method2 { errorCodeType error;
error = call method3;
if (error) return error;
else proceed;
}
errorCodeType method3 { errorCodeType error;
error = call readFile;
if (error) return error;
else proceed;
method1 { try {
call method2;
} catch (exception e) { doErrorProcessing;
} }
method2 throws exception { call method3;
}
method3 throws exception { call readFile;
}
45
AOT AOT
LAB LAB Classificazione degli errori
Le eccezioni sono oggetti, istanze di classi in una gerarchia
Es. Eccezioni per I/O discendono tutte da IOException
Discendenti per errori più specifici
FileNotFoundException se il file non si trova
catch (FileNotFoundException e) { // ...
} catch (IOException e) { // ...
}
AOT AOT
LAB LAB Costo dei bug
Trovare bug nei programmi non è un compito facile, e nemmeno una esperienza eccitante…
Molti bug possono capitare a causa di specifiche non ben chiare e capite
Barry Boehm: se trovare e correggere un problema in fase di specifica dei requisiti costa 1$, il costo passa a 5$ durante il progetto, $10 durante la programmazione,
$20 durante lo unit testing, e fino a $200 dopo la consegna del sistema
Meglio far emergere i bug nelle prime fasi dello sviluppo!
Agent and Object Technology Lab Dipartimento di Ingegneria dell’Informazione
Università degli Studi di Parma
AOT AOT LAB LAB
Asserzioni - Sommario
Sintassi
Precondizioni
Postcondizioni
Invarianti di classe
Abilitazione
AOT AOT
LAB LAB Asserzioni in Java
Nelle prime versioni di Java, non esisteva un
meccanismo per le asserzioni, occorreva ricorrere a soluzioni ad hoc
Asserzioni aggiunte a Java a partire dalla versione 1.4
Possibile attivarle o disattivarle a runtime
Non attraverso una libreria, ma un costrutto del linguaggio, nuova parola riservata: assert
Non si tratta di una soluzione completa di tipo design-by- contract, ma una forma limitata
Altrimenti cambi sostanziali al linguaggio, alterando la semplicità di Java
49
AOT AOT
LAB LAB Sintassi delle asserzioni
L’istruzione di asserzione può assumere due forme 1. assert expr1;
expr1: espressione booleana, se falsa lanciata AssertionError senza messaggio dettagliato
2. assert expr1 : expr2;
expr2 espressione con un valore
Può includere chiamate a metodi, ma non void
Usata per fornire dettagli al costruttore diAssertionError
AOT AOT
LAB LAB Inserire asserzioni nel codice
Ci sono situazioni in cui è utile inserire asserzioni
Invarianti interne e di controllo del flusso
Precondizioni, postcondizioni e invarianti di classe
Ci sono anche situazioni in cui sono inappropriate Non usare le asserzioni per
controllare gli argomenti di metodi pubblici
Tipicamente parte delle specifiche (o contratto) del metodo
Da rispettare con asserzioni attive o no
Argomenti sbagliati => eccezioni di runtime appropriate
IllegalArgumentException,
51
AOT AOT
LAB LAB Effetti collaterali
Non usare le asserzioni per qualsiasi cosa
necessaria al corretto funzionamento dell’applicazione
Le asserzioni possono essere disabilitate
L’espressione booleana potrebbe non venir valutata
Le espressione dovrebbero essere prive di effetti collaterali
Non dovrebbero avere effetti visibili successivi alla sua valutazione
Possono modificare stato usato solo in altre asserzioni
AOT AOT
LAB LAB Effetti collaterali
Programma che funziona solo con asserzioni attivate
// Broken! - action is contained in assertion assert names.remove(null);
Forma corretta: eseguire l’azione prima dell’asserzione
// Fixed - action precedes assertion
boolean nullsRemoved = names.remove(null);
assert nullsRemoved;
53
AOT AOT
LAB LAB Invarianti interne
switch (suit) { case Suit.CLUBS:
...
break;
case Suit.DIAMONDS:
...
break;
case Suit.HEARTS:
...
break;
case Suit.SPADES:
...
default: // add default just to assert: it should not happen!
assert false : suit;
// throw new AssertionError(suit);
// alternative: offers protection // even if assertions are disabled }
AOT AOT LAB LAB
if (i % 3 == 0) { . . .
} else if (i % 3 == 1) { . . .
} else {
assert i % 3 == 2 : i;
// to state an assumption, here
// instead of a comment, use an assertion!
. . .
Invarianti interne e sul flusso
void foo() { for (...) {
if (...) return;
}
assert false; // Execution should never reach this point!
}
55
AOT AOT
LAB LAB Precondizioni
Per convenzione, controlli espliciti su precondizioni di metodi pubblici, lancio specifiche eccezioni
Convenzione sempre valida: non usare asserzioni per controllare i parametri di un metodo pubblico
Controllare sempre gli argomenti, anche se asserzioni disabilitate
Asserzioni non lanciano eccezioni specifiche, ma solo AssertionError
/**
* Sets the refresh rate.
* @param rate refresh rate, in frames per second.
* @throws IllegalArgumentException if rate <= 0 or rate > MAX_REFRESH_RATE.
*/
public void setRefreshRate(int rate) {
// Enforce specified precondition in public method if (rate <= 0 || rate > MAX_REFRESH_RATE)
throw new IllegalArgumentException("Illegal rate: " + rate);
setRefreshInterval(1000/rate);
}
AOT AOT
LAB LAB Precondizioni
Le asserzioni, invece, vanno bene per testare precondizioni di metodi non pubblici
/**
* Sets the refresh interval (which must correspond to a legal frame rate).
* @param interval refresh interval in milliseconds.
*/
private void setRefreshInterval(int interval) {
// Confirm adherence to precondition in nonpublic method
assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;
... // Set the refresh interval }
57
AOT AOT
LAB LAB Postcondizioni
/**
* Returns a BigInteger whose value is (this-1 mod m).
* @param m the modulus.
* @return this-1 mod m.
* @throws ArithmeticException m <= 0, or this BigInteger
* has no multiplicative inverse mod m (that is, this
* BigInteger is not relatively prime to m).
*/
public BigInteger modInverse(BigInteger m) { if (m.signum <= 0)
throw new ArithmeticException("Modulus not positive: " + m);
... // Do the computation
// You can test postcondition with assertions // in both public and nonpublic methods
assert this.multiply(result).mod(m).equals(ONE) : this;
return result;
}
AOT AOT
LAB LAB Memorizzare lo stato iniziale
void foo(final int[] array) {
// Inner class to save state and perform final consistency check class DataCopy {
private int[] arrayCopy;
DataCopy() {
arrayCopy = (int[]) array.clone();
}
boolean isConsistent() {
return Arrays.equals(array, arrayCopy);
} }
DataCopy copy = null;
// Always succeeds; has side effect of saving a copy of array assert ((copy = new DataCopy()) != null);
... // Manipulate array
// Ensure array has same values as before manipulation.
assert copy.isConsistent();
59
AOT AOT
LAB LAB Invariante di classe
Tipo di invariante interna che vale per ogni istanza, eccetto che durante le transizioni tra stati consistenti
Può specificare relazioni tra più attributi
Dovrebbe essere vera prima e dopo ogni metodo pubblico
Es. Imporre che un albero sia sempre bilanciato e ordinato
Singolo metodo privato chiamato da asserzioni
Asserzione all’uscita di ogni metodo e costruttore pubblico
Se stato incapsulato, non necessaria asserzione all’inizio
// Class invariant: returns true if this tree is properly balanced private boolean balanced() {
. . . }
public someMethod() { . . .
assert balanced();
}
AOT AOT
LAB LAB Abilitare le asserzioni
Asserzioni disabilitate per default a runtime
Per abilitarle, switch -enableassertions, o -ea,
Per disabilitarle, switch -disableassertions, o –da
Si può specificare una granularità, indicando una classe o un package
Es. Lanciare un programma, BatTutor, con asserzioni abilitate solo nel package com.wombat.fruitbat (e nei sottopackage):
java -ea:com.wombat.fruitbat... BatTutor
61
AOT AOT
LAB LAB Design-by-contract - Perché no?
Perchè non fornire funzionalità di tipo design-by-contract complete, come in Eiffel?
Ipotesi considerata
Ma grossi cambi al linguaggio e alle librerie
Gravi inconsistenze tra vecchie e nuove librerie
Non preservata la semplicità caratteristica di Java
Semplice meccanismo delle asserzioni
Forma limitata di design-by-contract
Appropriata per precondizioni non pubbliche, postcondizioni e invarianti di classe
Precondizioni pubbliche con eccezioni documentate, come
IllegalArgumentException e IllegalStateException