Astrazione procedurale

09/05/2014
Astrazioni nella
progettazione del software:
Astrazioni funzionali
JML
by contract"
Astrazione
Astrazione: «ignorare dettagli non
essenziali per lo scopo che si vuole
raggiungere»
L informatica (e non solo) è basata
interamente sul concetto di astrazione:
Esempio di astrazioni nella
programmazione in-the-small:
Concetto di variabile e di tipo
Istruzioni di controllo (while, if, ...)
2
1
09/05/2014
Tipi di astrazioni per progettazione
SW
Astrazioni sulle operazioni nuove operazioni
Procedural abstractions
Astrazioni sui dati
(Tipi di dati astratti)
nuovi tipi di dati
Data abstractions (Abstract data types)
Astrazioni sul controllo
Iteratori
nuovi tipi di iterazioni
Astrazione da tipi individuali a famiglie di tipi
Ereditarietà
3
Meccanismi di astrazione nella
progettazione del software (1)
1. Abstraction by parameterization
Generalizza moduli in modo che
siano utilizzabili su dati diversi
Tramite parametri formali,
sottoprogramma astrae dai valori
effettivi dei parametri attuali, ma ne
considera solo presenza, numero e
tipo
4
2
09/05/2014
Esempio (1)
Codice ripetuto con differenti identificatori
ma stessa funzione:
if (a<0) a=(-a);
.
.
if (varB<0) varB=(-varB);
.
.
if (anotherVar<0) anotherVar = (anotherVar);
5
Esempio (2)
Definiamo allora un astrazione e la richiamiamo ogni
volta applicandola a parametri:
public static int abs(int p) {
if (p<0) return -p;
return p;
}
a = abs(a);
..
varB = abs(varB);
..
anotherVar = abs(anotherVar); .
Abstraction by parameterization!!
6
3
09/05/2014
Meccanismi di astrazione nella
progettazione del software (2)
2. Abstraction by specification
Non importa come il codice implementa una
certa funzione od operazione: basta che si
comporti come ci si aspetta
Specifica è descrizione di che cosa esegue
il modulo, indipendentemente dal come
la specifica del sottoprogramma consente di
ignorare l'algoritmo "incapsulato" nel
sottoprogramma
7
Esempio: diverse implementazioni
public static int abs(int p) {
if (p<0) return -p;
return p;
}
public static int abs(int p) {
return (p<0) ? p : p ;}
public static int abs(int p) {return p *sgn(p);}
public static int abs(int p) {return sqrt(p*p);}
Rappresentano tutte la stessa astrazione: il valore
assoluto!
8
4
09/05/2014
Esempio
Abstraction by
specification
(specifica= sintassi
+ semantica)
/* restituisce valore assoluto di p */
public static int abs(int p) {
if (p<0) return -p;
Abstraction by
return p;
parameterization
}
Implementazione
(del tutto irrilevante,
purche corretta!!)
9
Vantaggi di Abstraction by
Specification
Località
la specifica può essere letta o scritta senza la
necessità di esaminare l'implementazione
consente l'indipendenza dei programmatori: basta
mettersi d'accordo sulla specifica
Modificabilità
l'astrazione può essere reimplementata senza
effetti sui clienti ("non sorprendere gli utilizzatori")
consente manutenzione semplice e economica
quando la specifica non cambia
10
5
09/05/2014
Astrazione procedurale
Definisce tramite una specifica (abstraction by
specification) un operazione complessa su dati
generici (o parametri: abstraction by parameterization)
rimozione di duplicati da un Vector
calcolo del fattoriale di un intero
... sort(), sqrt(), abs()
Può in generale avere diverse implementazioni, che
ne rispettino la specifica.
In Java: corrisponde a specifica di un metodo static
Si può dire che un astrazione procedurale rappresenta
la classe d equivalenza di tutti i metodi statici che
implementano la stessa specifica
11
Specifica delle astrazioni procedurali
Specifica data usando il linguaggio naturale in
maniera sistematica oppure una semplice
notazione matematica
JML (Java Modeling Language)
La specifica deve essere chiara e precisa, in modo
da non lasciare spazio ad ambiguità
requires o precondizione (pre)
condizioni sui parametri sotto le quali la specifica è
definita
ensures o postcondizione (post)
effetto garantito al termine dell esecuzione
dell astrazione, sotto l ipotesi che la precondizione sia
12
verificata
6
09/05/2014
Esempio Pre e postcondizioni
/*requires valore >=0 del parametro x
*ensures restituisce la radice quadrata di x */
public static float sqrt(float x)
Cosa succede se x <0?
Il metodo sqrt non è definito
Qualunque comportamento è accettabile
perché non deve mai succedere che la
precondizione non è verificata
13
Pre e Postcondizioni
Definizione
La precondizione di un metodo ci dice che cosa deve
essere vero per poterlo chiamare
La postcondizione normale ci dice che cosa deve
essere vero quando il metodo ritorna normalmente
(senza sollevare eccezioni)
La postcondizione eccezionale ci dice che cosa è
vero quando il metodo ritorna sollevando
un eccezione
/*@ signals (IllegalArgumentException e) x < 0;
@*/
14
7
09/05/2014
Programmazione per contratto
Specifica o Contratto
Contratto è termine usato spesso per
descrivere la specifica di astrazioni
procedurali (ma anche interfacce di
moduli ecc.)
Pre-conditions ( requires )
Post-conditions ( ensures )
15
Contratti per il software
/*@ requires x >= 0.0;
@ ensures JMLDouble.approximatelyEqualTo(x,
@
\result * \result, eps);
@*/
Obblighi
Cliente
Implementazion
e
Diritti
Passare param.
non negativo
Ottenere radice
quadrata
approssimata
Calcolare e ritornare
la radice quadrata
Ricevere argomenti
non negativi
16
8
09/05/2014
Modello matematico
Un metodo definisce una relazione tra
precondizione
0
0
postcondizione
100
10
-10
Input
Output
17
Contratti e Abstraction by Specification
Un contratto può essere soddisfatto in molti modi
Per esempio, square root:
Linear search
Binary search
Metodo di Newton
Ciò che cambia sono le proprietà non funzionali
Efficienza
Consumo di memoria
Un contratto pertanto astrae da tutte le implementazioni,
che posso cambiare nel seguito
18
9
09/05/2014
Contratti come documentazione
Per ciascun metodo va definito:
Che cosa esso richiede
Che cosa esso assicura
I contratti sono:
più astratti del codice,
non necessariamente costruttivi,
spesso verificabili meccanicamente, così da favorire il
debugging
nel caso di verificabilità meccanica i contratti sono
sempre aggiornati
19
Ulteriori vantaggi
Attribuzione delle colpe
Chi è il colpevole se:
la precondizione è violata?
la postcondizione è violata?
Evita inefficienti check difensivi
//@ requires a != null && (* a is sorted *);
public static int binarySearch(Thing[] a, Thing x)
20
10
09/05/2014
Regole di ragionamento
Codice cliente
Deve funzionare per ogni implementazione che soddisfi il
contratto
Può dunque solo far riferimento al contratto (NON al codice!),
cioè
deve assicurare la precondizione
deve prendersi carico della postcondizione
//@ assert 9.0 >= 0;
double result = sqrt(9.0);
Codice dell implementazione
deve soddisfare il contratto, e cioè prendersi carico della
precondizione e assicurare la postcondizione
ma può fare qualunque cosa purchè permessa dal contratto
21
Commenti adeguati per descrivere
contratti?
Se specifiche scritte solo con commenti si pone il
problema di come essere certi che le specifiche siano
esatte e non ambigue, al fine di verificare poi che
l implementazione sia corretta
Esempio:
\\se x>=0 restituisce la radice quadrata del parametro x
public static float sqrt(float x)
E soddisfacente?
In realtà, la funzione calcola un valore approssimato della
radice di x. Però trattandosi di una funzione matematica
sarebbe anche utile che dicesse qual è l approssimazione nel
calcolo.
In generale un commento in linguaggio naturale può
essere ambiguo o addirittura errato
Come garantire che l implementazione rispetti la
specifica?
22
11
09/05/2014
Linguaggi per i contratti
Esistono notazioni matematiche per scrivere le specifiche.
Vantaggi:
Le specifiche non sono ambigue (perche la notazione ha un
significato univoco, a differenza del linguaggio naturale)
il codice e le specifiche possono essere verificate se la notazione è
eseguibile
Esecuzione delle specifiche: nel codice Java compilato è
possibile inserire controlli («asserzioni») che verificano a run time
che la specifica di un metodo (o una sua parte) sia verificata
se non è verificata viene lanciata eccezione
Questo è molto utile durante le fasi di testing e debugging:
p.es., testando un metodo con alcuni dati, se la specifica non è
verificata su quei dati, possiamo saperlo senza guardare i risultati
dell esecuzione ma solo controllando se si verificano eccezioni
23
JML: Java Modeling Language
Linguaggio per la descrizione formale delle specifche,
specializzato per Java
Useremo una versione semplificata e modificata
Le specifiche JML sono contenute in annotazioni,
rappresentati con la sintassi dei commenti:
oppure
@*/
I simboli at (@) all inizio di una linea sono ignorati all interno
delle annotazioni
Domanda: quale vantaggio dall uso di annotazioni?
24
12
09/05/2014
Asserzioni in JML
Asserzioni JML sono espr. booleane Java, ma:
non possono avere side-effect
non si può usare =, ++, --, etc.
possono solo chiamare metodi puri (ossia senza side
effects)
possono usare alcune estensioni a Java \result, \
Sintassi
a ==> b
a <== b
a <==> b
a <=!=> b
\old(E)
Semantica
a implica b
b implica a
a iff b
!(a <==> b)
valore di E nel pre-stato
25
Quantificatori
JML supporta diversi quantificatori
Universale and esistenziale (\forall and \exists)
Funzioni quantificatrici (\sum, \product, \min, \max)
Quantificatore numerico (\num_of)
(\forall Student s; juniors.contains(s); s.getAdvisor() != null)
(\forall Student s; ; juniors.contains(s) ==> s.getAdvisor() != null)
2
6
13
09/05/2014
requires e ensures
//@ requires in >= 0;
//@ ensures Math.abs(\result*\result-in)<0.0001;
public static float sqrt (float in);
Clausola requires stabilisce la precondizione
Clausola ensures la postcondizione
\result nella clausola ensures indica il valore restituito al
termine dell esecuzione del metodo
27
Omettere pre/post
condizioni
Se la clausola requires è omessa, il metodo non
ha nessuna precondizione (cioè non impone
nessuna condizione sui parametri)
è come scrivere requires true
Se la clausola ensures è omessa, il metodo non
ha nessuna postcondizione: il metodo non
promette nulla per i propri risultati (quindi
qualunque cosa faccia va bene...non è molto
utile)
è come scrivere ensures true
15
14
09/05/2014
Gestire l informalità
In caso di bisogno si possono inserire commenti
(* ... *) vale sempre true
//@ ensures (* a è una permutazione del valore
//@ originale*)
//@ && (* a è ordinato per valori crescenti *);
Si perde l'eseguibilità, ma a volte è troppo complesso
Si perde l'eseguibilità, ma a volte è troppo complesso
scrivere specifiche complete in JML
scrivere specifiche complete in JML
Nei commenti si cerca comunque di mantenere un struttura simil-JML
Nei commenti si cerca comunque di mantenere un struttura simil-JML
16
Descrizioni miste
Si può spesso scrivere una postcondizione JML più
debole di quella corretta, ma ancora significativa,
piuttosto di scrivere solo un commento
public class IMath {
/*@ requires x>=0;
@ ensures \result >= 0 &&
@ (* \result is an int approximation to square root of x *)
@*/
}
30
15
09/05/2014
Asserzioni in Java
Asserzione: espressione booleana da verificare a
run-time
se espressione è vera allora si continua
se espressione è false sistema genera errore:
AssertionError, non recuperabile, e il programma termina
Asserzioni possono essere abilitate (o lasciate
disabilitate) in fase di compilazione (di solito, usate
durante testing, disabilitate al rilascio):
Sintassi: assert <espressione boolena>;
Asserzioni possono essere usate ad esempio per
verificare le postcondizioni
in certi casi anche per precondizioni di metodi private, allo scopo di
testing. In Java non si usano asserzioni per le precondizioni dei metodi
pubblici
31
Es. descrizione mista e asserzioni
if x in a \result è un indice a cui si trova x, else \result==-1
Riscriverla così:
//@ ensures (*x e in a*) && x==a[\result]
//@ || (* x non e in a *) && \result ==-1;
Così è possibile inserire dei check durante il testing:
assert(i == -1 || a[i] ==x);
return i; }
Se l implementazione restituisse un valore che non
corrisponde all indice a cui si trova x, si otterrebbe un
AssertionError
Però l implementazione {return -1;} non causerebbe errori!
32
16
09/05/2014
Se gli oggetti riferiti dalle variabili
sono modificabili?
Per denotare lo stato delle variabili prima e dopo
l'operazione, usare \old(espressione)
Restituisce il valore che ha espressione al
momento della chiamata
Esempio: Metodo che inverte stato di accensione di
un Automobile
//@ ensures p.accesa() <==> !\old(p.accesa())
public static int invertiStato(Automobile p) {
}
in uscita p. accesa() == true sse \old(p.accesa) ==
false
33
assignable
Per segnalare che un parametro può essere
modificato si usa assignable
//@assignable a[*];
//@ ensures (* a è una permutazione del valore originale di a*)
//@ && (* a è ordinato per valori crescenti *)
public static void sort (int[] a)
Se un metodo non ha side-effects si può scrivere
assignable \nothing.
//@ assignable \nothing;
//@ ensures (*x e in a*) && x==a[\result]
//@ || (* x non e in a *) && \result ==-1;
public static int search (int[ ] a, int x)
34
17
09/05/2014
Omettere assignable
Se la clausola assignable è omessa, significa
che non vi è nessuna "promessa" sulla
modifica (e quindi non si può supporre nulla)
sono tutti assignable
18
Come progettare astrazioni
procedurali?
Generalità: specifiche devono essere il più generali possibile
es. usare parametrizzazione (v. search: definita per array di
qualunque lunghezza)
Minimalità: imporre il minimo di vincoli sul comportamento
La specifica deve lasciare la massima libertà implementativa
possibile
Se troppo dettagliata vincola l implementatore
Se troppo generica, ne limita l utilità
in particolare NON deve specificare COME risolvere il problema
Le specifiche possono essere indeterminate
Es. Search: quale indice viene ritornato nel caso di occorrenze
multiple? (non essendo rilevante non viene specificato)
implementazione è invece deterministica: produce sempre gli stessi
risultati sugli stessi ingressi (es. search ritorna indice più piccolo in
caso di occorrenze multiple). Però variando implementazione
risultati possono essere diversi per i casi indeterminati
(implementation-dependent)
36
18
09/05/2014
Ragionare sulle astrazioni procedurali
Scrivendo o rileggendo un programmasi cerca di convincersi
che sia corretto, ragionandovi
Ragionare sulle procedure: data precondizione, ci si convince
che il codice della procedura ha gli effetti desiderati
//@ requires a != null && (* a ordinato per valori crescenti*);
//@ ensures (*if x in a ritorna l'indice, else -1*);
//@ assignable \nothing;
public static int searchSorted (int[] a, int x)) {
for (int i =0; i<a.length; i++) //cerca x o elemento > x
if (a[i]==x) return i;
else if (a[i]>x) return 1;
return 1;
}
37
Eccezioni
ritorno
normale
ensures
----
ritorno
eccezionale
(throw)
----
3
8
19
09/05/2014
Eccezioni
La precondizione di un metodo ci dice che
cosa deve essere vero per poterlo
chiamare
La postcondizione normale ci dice che cosa
deve essere vero quando il metodo ritorna
normalmente (senza sollevare eccezioni)
La postcondizione eccezionale ci dice che
cosa è vero quando il metodo ritorna
sollevando un eccezione
/*@ signals (IllegalArgumentException e) x < 0;
@*/
3
9
Esempio
//@ assignable \nothing;
//@ ensures x == a[\result];
//@ signals (NotFoundException e) (* x non e presente in a *);
public static int cerca(int x, int [] a) throws NotFoundException
Significato: al termine dell esecuzione si può essere in uno dei due
casi:
1. la postcondizione è vera e nessuna eccezione è lanciata
2. è lanciata un eccezione ed è vera la condizione della signals corrispondente
Quindi, al termine della cerca, può valere x == a[\result]; senza
lanciare eccezione, oppure:
(* x non e presente in a *) e viene lanciata eccezione
Occorre prevedere una clausola signal per ogni eccezione che il
metodo può lanciare (sia checked che unchecked) altrimenti è come
scrivere una signals con una postcondizione true
40
20
09/05/2014
Semantica della \signals
//@ ensures A;
//@ signals (E e) B
è come avere postcondizione:
A && (*nessuna eccezione*)
|| B && (*lancia eccezione e*)
Attenzione: può essere lanciata eccezione se, oltre a
B, vale anche la postcondizione A
E compito dello specificatore distinguere i due casi, ad
NB: Il predicato B della signal può far riferimento sia
all oggetto eccezione e che all interfaccia del metodo
41
Schema completo
What is required? (p.es. Input diverso da null)
What is modified? (any inputs changed?)
When exceptions are thrown?
Useremo lo schema seguente:
visibility class c_name {
//OVERVIEW: commento generale sulla classe
//@ assignable
//@ requires
//@ ensures
//@ signals
}
42
21
09/05/2014
Precondizioni o eccezioni?
Precondizioni: procedure parziali
Procedura è parziale se ha precondizione non vuota
Ha un comportamento specificato solo per un sottoinsieme
del dominio degli argomenti
Per esempio
//@ requires n >= 0;
//@ ensures (* \result è il fattoriale di n *);
public static int fact (int n)
Ciò rende l'operazione poco sicura
che succede se i parametri non rispettano il vincolo?
esempio: la procedura per n<0 calcola un valore scorretto che poi è
usato da altre parti del programma: errore si propaga a tutto il
programma, rovinando i risultati, i dati memorizzati, ecc. Sarebbe
meglio se la procedura segnalasse al chiamante il problema. 44
22
09/05/2014
Procedure parziali e robustezza
Le procedure parziali compromettono la robustezza
dei programmi
un programma è robusto se, anche in presenza di errori o
situazioni impreviste, ha un comportamento ragionevole (o,
per lo meno, ben definito)
per le procedure parziali il comportamento al di fuori delle
precondizioni è semplicemente non definito dalla specifica
se una procedura non è definita per alcuni valori (in quanto
inattesi ), si ottengono errori run-time o, peggio,
comportamenti imprevedibili quando tali valori sono passati
come paramentri
Per ottenere programmi robusti, le procedure devono
essere totali !!!
45
Eccezioni o precondizioni
Per metodi pubblici di classi pubbliche, buona norma
eliminare la clausola requires e lanciare eccezioni
quando requires è violata. Le eccezioni si includono
nella clausola signalsIn Java
public static int fact (int n) throws NegativeException{
if (n<0) throw new NegativeException
46
23
09/05/2014
Es. requires o eccezioni
//@ requires x != null;
//@ ensures a[\result].equals(x);
//@ signals (NotFoundException e) (*x non e in a *);
public static int cerca(String x, String[] a) throws NotFoundException
Versione migliore : lancia eccezione anche se x è null
//@ ensures x != null && a[\result].equals(x);
//@ signals (NotFoundException e) (*x non e in a *)
//@ signals (NullPointerException e) x == null;
public static int cerca(String x, String[] a) throws NullPointerException,
NotFoundException
47
Quando non lanciare eccezioni?
Es: Ricerca binaria
//@ requires (*a ordinato crescente *);
//@ ensures a[\result] == x;
//@ signals (NotFoundException e)
//@
(* x non e in a *);
L inserimento della clausola signals comporta che un metodo che
implementa la specifica ne verifichi la condizione per decidere se
lanciare l eccezione.
La verifica della condizione requires richiede più tempo che l esecuzione
della cerca!
La verifica dell ordinamento richiede un tempo proporzionale a a.length,
mentre ricerca binaria richiede log(a.length):
non è conveniente verificare la precondizione, se non durante la fase di
testing.
In genere, non si lancia eccezione quando il chiamante può fare il
controllo della precondizione molto meglio del chiamato o quando
controllo è molto difficile/inefficiente.
Questo indebolisce la sicurezza del programma, ma può essere utile
compromesso con efficienza
Controllo può essere inserito solo in fase
48
24
09/05/2014
Quantificatori in JML
\forall, \exists, \num_of,...
Elementi delle collezioni
Per parlare degli elementi di una collezione, si possono
usare i metodi pubblici che non hanno side
effect:equals, contains, containsAll, get, sublist
//@ ensures (* a è una permutazione di \old(a)*)
//@ && (* a ordinato per valori crescenti*);
public static void sort (ArrayList<Integer> a)
//@ ensures a.containsAll(\old(a)) && \old(a).containsAll(a)
//@ && (* a ordinato per valori crescenti*);
public static void sort (ArrayList<Integer> a)
Usiamo convenzione (non JML): se var. x è di tipo riferimento,
\old(x) è un riferimento alll oggetto nello stato al momento
della chiamata del metodo.
50
25
09/05/2014
Quantificatori espliciti
Metodi contains ecc. delle collezioni spesso non bastano
es: come scrivere a ordinato per valori crescenti ?
JML supporta diversi quantificatori
Universale and esistenziale (\forall and \exists)
Funzioni quantificatrici (\sum, \product, \min, \max)
Quantificatore numerico (\num_of)
(\forall Student s; juniors.contains(s); s.getAdvisor() != null)
51
\forall
Sintassi (simile per tutti i quantificatori):
(\forall variabile; range; condizione)
Semantica: per tutti i possibili valori della variabile che
soddisfano range, condizione deve essere true
Esempio: a ordinato per valori crescenti
(\forall int i; 0<=i && i< a.length-1; a[i]<=a[i+1])
Equivalente a
(\forall int i; ; 0<=i && i< a.length-1 ==> a[i]<=a[i+1])
52
26
09/05/2014
\exists
//@ ensures
//@ (\exists int i; 0<=i && i<a.length;a[i] == x)
//@ ? x == a[\result]
//@ : \result == -1;
//@ assignable \nothing;
public static int cerca(int x, int [] a)
Usa operatore logico di Java ( ? : ) che funziona
come un if then else
53
num_of
(\num_of int i; P(i); Q(i))
il numero totale di i per cui vale P(i) && Q(i)
Esempio: numero di elementi positivi in array a :
(\numof int i; 0<=i&& i<a.length; a[i]>0)
Es: nessun elemento di a compare più di due
volte in a
(\forall int i; 0<=i && i<a.length;
(\numof int j; i<j && j<a.length; a[i]==a[j])
<=1));
54
27
09/05/2014
sommatorie, produttorie...
Li definiamo mediante esempi illustrativi
(\sum int i; 0 <= i && i < 5; i) == 0 + 1 + 2 + 3 + 4
(\product int i; 0 < i && i < 5; i) == 1 * 2 * 3 * 4
(\max int i; 0 <= i && i < 5; i) == 4
(\min int i; 0 <= i && i < 5; i-1) == -1
55
28