Note
Un’astrazione procedurale, definisce, tramite una specifica, un’operazione complessa su dati generici (o parametrici).
Specifica
Una specifica è una descrizione di un’entità che prescinde dall’implementazione. Nel nostro caso è un astrazione del frammento di codice, e rappresenta la classe d’equivalenza di tutte le implementazioni possibili.
In Java la signature di un metodo ne esprime la sintassi, tuttavia non ci dice nulla circa la semantica. È vantaggiosa poiché:
- L’implementazione può essere letta o scritta senza la necessità di analizzare le implementazioni di altre astrazioni.
- L’astrazione può essere re-implementata senza effetti sulle astrazioni che la usano.
È data usando un linguaggio naturale o una semplice notazione matematica.
Java Modelling Language
Note
Java Modelling Language (JML) è un linguaggio formale di specifica basato su Java, non eseguibile dal compilatore. Le sue asserzioni possono:
- Predicare su variabili
- Quantificare su variabili
- Invocare metodi puri
- Predicare sull’invarianza di alcune variabili
- Predicare sul corretto lancio delle eccezioni
Le specifiche JML sono aggiunte al codice Java tramite commenti, che cominciano con il simbolo
@://@ <JML Specification> /*@ <JML Specification> @*>Funziona definendo delle precondizioni (condizioni sui parametri sotto le quali la specifica è definita e valida) e delle postcondizioni (effetti garantiti al termine dell’esecuzione dell’astrazione).
Programmazione per contratto
La presenta di pre e post condizioni introduce il tema della programmazione per contratto, dove possono variare solamente le proprietà non funzionali, che non sono vincolate da esso.
Le specifiche devono essere:
- Generali: utilizzabili anche al di fuori del progetto specifico.
- Minimali: slegate il più possibile dall’implementazione.
- Robuste: il comportamento anomale deve essere previsto e gestito, e quindi la funzione definita deve essere totale.
I vincoli esprimibili da JML si categorizzano in:
- modifiers
- requires
- assignable
- ensures e result
- signals
Modifiers
I modifiers sono delle etichette che descrivono le caratteristiche del metodo o delle variabili su cui si stanno imponendo i vincoli.
puresi impone su metodi, e indica che il metodo non ha side effects.//@ pure boolean isSorted(int[] a) { ... }
modelsi impone su una variabile, e permette di introdurre una variabile logica astratta che non esiste nel bytecode del programma. Questa non assume né valori né stati, e funge solo da etichetta.//@ model int size;
ghostsi impone su una variabile, e comemodelintroduce una variabile non esistente nel Bytecode, tuttavia ha un valore e può cambiare durante il programma.//@ ghost int counter;Per assegnare alle variabili
ghostsi usa il modifierset.//@ set counter = counter + 1;
Requires
requiresimpone delle precondizioni, e può essere omesso in caso una funzione sia totale.//@ requires ...;In caso la definizione di requires sia particolarmente complesso è opportuno ometterle e utilizzare
signals.
Assignable
assignablesegnala che un’entità è mutabile, e quindi può essere modificata durante la chiamata del metodo. Si applica a parametri dei metodi, variabili, oggetti e attributi degli oggetti.//@ assignable nameVar;È possibile specificare che nessuna variabile che rientra nel campo di visibilità di JML viene modificata:
//@ assignable \nothing:Talvolta può essere utile usare il predicato
\old.
Ensures e result
Indica le post condizioni tramite:
//@ ensures ...Al suo interno si può usare il predicato
\resultche indica l’output, comparatori matematiche, formule aritmetiche complesse, metodi puri, oppure un insieme di valori indicati://@ ensures \result == [valore]; //@ ensures Math.abs(\result * \result) < 0.1;
Signals
I Signals permettono di verificare post-condizioni eccezionali lanciate in caso di violazione delle requires:
//@ signals (IllegalArgumentException e) x < 0;Queste si specificano indicando il tipo di eccezione e la condizione logica che indica quanto verrà lanciata.
In caso esista solo un tipo di eccezione lanciabile dal metodo si può usare:
//@ signals_only (IllegalArgumentException e) x < 0;
È necessario che le condizioni delle Signals e Ensures siano ortogonali tra loro.
Altre clasusole e commenti
Esistono dei predicati in JML, oltre a quelli visti, che non rientrano in alcuna categoria, questi sono:
a ==> b:aimplicab.a <== b:bimplicaa.a <==> b:ase e solo seb.a <=!=> b: notase e solo seb.\old(E): valore diEprecedente all’esecuzione del codice.(* ... *): commenti testuali, da usare se i vincoli sono estremamente difficili da esprimere.
Quantificatori
JML supporta diversi tipi di quantificatori, tra cui:
- Universale (
\forall) ed Esistenziale (\exists).- Funzioni quantificatrici (
\sum,\product,\min,\max).- Quantificatore numerico (
\num_of).In JML questi hanno una forma a tre campi:
(\forall Tipo v; range; condizione)Dove il primo campo indica il tipo e il nome della variabile su cui predichiamo, il secondo campo indica il dominio ristretto in cui JML deve operare, e l’ultimo campo è il predicato che deve essere soddisfatto da ogni elemento del range.
Nelle funzioni quantificatrici e quantificatore numerico l’ultimo campo agisce come filtro che determina quali valori contare.
Astrazioni sui dati
Note
Con JML è possibile descrivere un nuovo tipo di dato astratto indicando valori e operazioni possibili. In questo modo, astraendo dai dettagli di rappresentazione dei valori e d’implementazione delle operazioni, il resto del programma dipende solo dalla specifica del tipo.
Siccome nelle classi i metodi possono agire sulle variabili di stato, non basta quanto visto per le astrazioni procedurali.
Si nota che i metodi pubblici puri (che non introducono side effects) sono indicati con /*@ pure @*/.
Example
Un esempio di metodo puro è size, che restituisce il numero di elementi di un contenitore, mentre un esempio di metodo non puro è insert.
public int /*@ pure @*/ size() { ... } //@ ensures size()==\old(size())+1 public void insert(...) { ... }
Mutabilità
Un dato si dice mutabile quando ha metodi modificatori, cioè ha dei metodi che modificano lo stato dell’oggetto su cui vengono chiamati.
Specifica
spec_publicLa specifica
spec_publicpermette di utilizzare un oggetto astratto tipico (OAT), che è un’implementazione astratta, generalmente la più semplice possibile.//@ spec_public List<T> oat; //@ ensures \result == aot.size(); public /*@ pure @*/ int size() { ... }
Proprietà astratte
Le proprietà astratte predicano su cose che sono valide per tutti gli oggetti in generale, ne esistono due tipi:
- Proprietà invarianti: proprietà degli strati astratti invarianti.
- Proprietà evolutive: proprietà sulla relazione tra uno stato e quello successivo.