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
© Copyright 2024 Paperzz