Ragionamento sui tipi di dato Funzione di astrazione

  • Slides: 19
Download presentation
Ragionamento sui tipi di dato

Ragionamento sui tipi di dato

Funzione di astrazione (1) La funzione di astrazione cattura l’intenzione del progettista nello scegliere

Funzione di astrazione (1) La funzione di astrazione cattura l’intenzione del progettista nello scegliere una particolare rappresentazione. La funzione di astrazione AF: C --> A porta da uno stato concreto, lo stato di un oggetto della classe C, a uno stato astratto, lo stato dell’oggetto astratto. Per esempio la funzione di astrazione per l’implementazione di Int. Set porta vettori in insiemi, è una funzione molti-a-uno perché piú elementi della rappresentazione (vettori di interi) vengono portati nello stesso insieme (AF([1, 2]) = {1, 2}, ma anche (AF([2, 1]) = {1, 2}). L’oggetto da rappresentare può essere descritto da un oggetto astratto tipico della classe dato nella OVERVIEW della specifica. Possiamo usare la notazione matematica e la notazione del linguaggio di programmazione con opportune abbreviazioni.

Funzione di astrazione (2) Esempio. // Un tipico Int. Set è {x 1, …,

Funzione di astrazione (2) Esempio. // Un tipico Int. Set è {x 1, …, xn} // La funzione di astrazione è // AF(c) = { c. els[i]. int. Value | 0 <= i < c. els. size } Esempio. // Un tipico Poly è c 0 + c 1 x + c 2 x 2 + … // La funzione di astrazione è // AF(c) = c 0 + c 1 x + c 2 x 2 + … // dove ci = c. trms[i] if 0 <= i < c. trms. size // = 0 altrimenti

Invariante della rappresentazione (1) Il type checking di Java garantisce che ogni volta che

Invariante della rappresentazione (1) Il type checking di Java garantisce che ogni volta che si chiama un metodo o un costruttore il suo oggetto this appartiene alla sua classe. Tuttavia non tutti gli oggetti della classe sono necessariamente rappresentazioni legittime di oggetti astratti. Per esempio la rappresentazione di Int. Set potrebbe includere oggetti il cui vettore els contiene un elemento con lo stesso valore (quando abbiamo deciso che non sia cosí). Un invariante della rappresentazione o rep invariant è un predicato I: C --> boolean che è vero per gli oggetti che sono rappresentazioni legittime.

Invariante della rappresentazione (2) Esempio. Un invariante per Int. Set // // // L’invariante

Invariante della rappresentazione (2) Esempio. Un invariante per Int. Set // // // L’invariante della rappresentazione è c. els != null && per tutti gli interi i c. els [i] Integer && per tutti gli interi i, j ( 0 <= i < j < c. els. size ==> c. els [i]. int. Value != c. els [j]. int. Value ) Oppure meno formalmente // // L’invariante della rappresentazione è c. els != null && tutti gli elementi di c. els [i] sono interi && non ci sono duplicati in c. els

Invariante della rappresentazione (3) Esempio. Consideriamo una rappresentazione di Int. Set consistente di 100

Invariante della rappresentazione (3) Esempio. Consideriamo una rappresentazione di Int. Set consistente di 100 booleani piú un vettore. La variabile sz conserva il numero di elementi dell’insieme. private boolean[100] els; private Vector other. Els; private int sz; Un intero i nell’intervallo 0 -99 è rappresentato come true in els[i]. Interi fuori dell’intervallo sono rappresentati come nella precedente rappresentazione di Int. Set.

Invariante della rappresentazione (4) // La funzione di astrazione è // AF(c) = {

Invariante della rappresentazione (4) // La funzione di astrazione è // AF(c) = { c. other. Els [i]. int. Value | 0 <= i < c. other. Els. size // + // {j | 0 <= j < 100 && c. els[j] } // L’invariante della rappresentazione è // c. els != null && c. other. Els != null &&. els. size = 100 && // tutti gli elementi in c. other. Els sono Integers && // tutti gli elementi in c. other. Els non sono nell’intervallo 0 to 99 && // non ci sono duplicati in c. other. Els && // c. sz = c. other. Els. size + ( conteggio delle entrate true in c. els )

Invariante della rappresentazione (5) Esempio. L’implementazione di Poly che abbiamo dato memorizza i coefficienti

Invariante della rappresentazione (5) Esempio. L’implementazione di Poly che abbiamo dato memorizza i coefficienti fino al grado del polinomio senza zeri in coda (tranne che nel caso del polinomio zero). Gli array hanno almeno un elemento e il grado deve essere uno meno dell’ampiezza di trms. // // // L’invariante della rappresentazione è c. trms != null && c. trms. length >= 1 && c. deg = c. tmrs. length-1 && c. deg > 0 ==> c. trms [deg] != 0 Esempio. Se tutti gli oggetti concreti sono rappresentazioni legali si ha il seguente invariante: // L’invariante della rappresentazione è true L’invariante dovrebbe esprimere tutte le condizioni da cui dipendono le operazioni. Va scelto prima di implementare le operazioni e incluso come commento nel codice.

Implementazione della funzione di astrazione La funzione di astrazione dà l’interpretazione della rappresentazione. Porta

Implementazione della funzione di astrazione La funzione di astrazione dà l’interpretazione della rappresentazione. Porta ciascun oggetto della rappresentazione legale nell’oggetto astratto che intende rappresentare. Si può implementare usando il metodo to. String.

Implementazione dell’invariante della rappresentazione (1) Il metodo rep. Ok che verifica l’invariante ha la

Implementazione dell’invariante della rappresentazione (1) Il metodo rep. Ok che verifica l’invariante ha la seguente specifica public boolean rep. Ok ( ) // EFFECTS: Returns true if the rep invariant holds for this; // otherwise returns false. Il metodo è pubblico perché deve poter essere chiamato da codice fuori della sua classe. Ogni tipo dovrebbe fornire questo metodo. Non è necessario dare la specifica di ciascuno perchè è identica per ogni tipo. Il metodo rep. Ok può essere usato da programmi di test per verificare se una implementazione preserva l’invariante oppure può essere usato entro le implementazioni di costruttori e metodi. I costruttori possono chiamarlo prima di ritornare per assicurare che per l’oggetto inizializzato vale l’invariante. Ogni metodo che modifica la rappresentazione può chiamarlo per assicurare che ancora vale per gli oggetti modificati. Per esempio, dovrebbero chiamarlo insert e remove di Int. Set, add, mul, minus di Poly. Se l’invariante non vale si può lanciare Failure. Exception.

Implementazione dell’invariante della rappresentazione (2) // for Poly: public boolean rep. Ok( ) {

Implementazione dell’invariante della rappresentazione (2) // for Poly: public boolean rep. Ok( ) { if (trms == null || deg != trms. length - 1 || trms. length == 0) return false; if (deg == 0) return true; return trms [deg] != 0; } //for Int. Set: public boolean rep. Ok( ) { if (els == null) return false; for (int i = 0; i < els. size( ); i++) { Object x = els. get(i); if (! (x instanceof Integer)) return false; for (int j = i + 1; j < els. size( ); j++) if (x. equals (els. get(j))) return false; } return true; }

Prova della correttezza di un’implementazione (1) Correttezza delle procedure standalone. Si assume che valga

Prova della correttezza di un’implementazione (1) Correttezza delle procedure standalone. Si assume che valga la precondizione e si esamina il codice per convincersi che la procedura fa ciò che la clausola REQUIRES richiede. Correttezza dell’implementazione di un’ astrazione di dato. Occorre ragionare sul codice scritto al livello concreto (che manipola la rappresentazione) per convincersi che soddisfa la specifica che è scritta in termini degli oggetti astratti.

Prova della correttezza di un’implementazione (2) Come provare che l’implementazione soddisfa l’invariante. Dobbiamo mostrare

Prova della correttezza di un’implementazione (2) Come provare che l’implementazione soddisfa l’invariante. Dobbiamo mostrare che l’invariante vale per gli oggetti restituiti dal costruttore. Esempio. // // // L’invariante della rappresentazione è c. els != null && per tutti gli interi i c. els [i] è Integer && per tutti gli interi i, j ( 0 <= i < j < c. els. Size ==> c. els [i]. int. Value != c. els [j]. int. Value ) Il costruttore Int. Set dà un oggetto che soddisfa l’invariante perch�é il vettore creato è vuoto. Il metodo is. In preserva l’invariante perché possiamo assumere che l’invariante valga quando è chiamato e is. In non modifica this. Lo stesso per size, choose e get. Index. Il metodo insert preserva l’invariante perché aggiunge x a this solo se x non è già in this (ossia se get. Index restituisce -1).

Prova della correttezza di un’implementazione (3) Esempio. Per Poly l’invariante è il seguente //

Prova della correttezza di un’implementazione (3) Esempio. Per Poly l’invariante è il seguente // The rep invariant is // c. trms != null && c. trms. length >= 1 && c. deg = c. tmrs. length-1 // && c. deg > 0 ==> c. trms [deg] != 0 Il costruttore per Poly che produce il polinomio zero preserva l’invariante perchè crea un array di un elemento. Cosí fa l’altro costruttore. L’operazione mul preserva l’invariante perché l’invariante vale per this al momento della chiamata e vale per q se q non è nullo. Se this oppure q è il polinomio zero allora è costruita la rappresentazione appropriata. Se né this né q sono zero, allora il termine più alto dell’array nell’oggetto Poly restituito, che contiene il prodotto dei termini più alti, può essere zero.

Prova della correttezza delle operazioni Per provare che le operazioni oltre che preservare l’invariante

Prova della correttezza delle operazioni Per provare che le operazioni oltre che preservare l’invariante fanno quello che devono fare occorre ragionare sull’implementazione. Esempio. Prendiamo l’implementazione del metodo remove di Int. Set. Il metodo controlla che l’elemento cercato sia nel vettore. Se non c’è ritorna. Altrimenti rimuove dal vettore l’elemento trovato e se l’invariante vale prima della chiamata non ci sono nel vettore duplicazioni dell’elemento.

Esposizione della rappresentazione (1) Un punto fondamentale delle astrazioni di dato è di poter

Esposizione della rappresentazione (1) Un punto fondamentale delle astrazioni di dato è di poter fare ragionamento locale, ossia di assicurare che una classe è corretta solo esaminando il codice della classe. A questo scopo le rappresentazioni degli oggetti astratti non possono essere modificate fuori delle loro implementazioni Esporre la rappresentazione significa che l’implementazione rende componenti mutabili della rappresentazione accessibili a codice fuori della classe. Esempio. Si espone la rappresentazione non diciarando private variabili di istanza.

Esposizione della rappresentazione (2) Esempio. Prendiamo Intset con un metodo all. Els cosí specificato

Esposizione della rappresentazione (2) Esempio. Prendiamo Intset con un metodo all. Els cosí specificato e implementato. Public Vector all. Els ( ) // EFFECTS: restituisce un vettore contenente gli elementi di this, // ciascuno esattamente una volta in ordine arbitrario Public Vector all. Els ( ) { return els; } Questa implementazione consente all’utente di accedere direttamente alla componente els e poiché è mutabile di modificarla. Per evitarlo il metodo all. Els dovrebbe restituire una copia del vettore.

Adeguatezza del tipo di dato (1) Le operazioni di un’astrazione di dato cadono in

Adeguatezza del tipo di dato (1) Le operazioni di un’astrazione di dato cadono in quattro categorie: - creatori. Creano oggetti del loro tipo dal nulla senza prendere in input alcun oggetto del loro tipo. Esempio: i costruttori di insiemi e di polinomi. - produttori. Prendono in input oggetti del loro tipo e creano altri oggetti del loro tipo. Possono essere costruttori o metodi. Esempio: add e mul di Poly. - mutatori. Sono metodi che modificano oggetti del loro tipo. I titpi devono essere mutabili. Esempio: insert e remove di Int. Set. - osservatori. Sono metodi che prendono in input oggetti del loro tipo e creano altri oggetti di altri tipi. Sono usati per ottenere informazioni sugli oggetti. Esempio: size, is. In per Int. Set, degree per Poly.

Adeguatezza del tipo di dato (2) Adeguatezza vuol dire che il tipo fornisce operazioni

Adeguatezza del tipo di dato (2) Adeguatezza vuol dire che il tipo fornisce operazioni di almeno tre delle categorie, ossia creatori, osservatori, produttori se è immutabile e creatori, osservatori, mutatori se mutabile. Troppe operazioni rendono più difficile comprensione dell’astrazione e cambiamenti nell’implementazione. Se il tipo è adeguato le sue operazioni possono essere aumentate con procedure sono che metodi statici di qualche altra classe.