Object Constraint Language

Da Teknopedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

L'Object Constraint Language o OCL è un linguaggio di specifica formale inizialmente proposto come estensione per il linguaggio di modellazione object-oriented UML e successivamente (2003) entrato a far parte del nuovo standard del linguaggio (UML 2.0). OCL può essere impiegato insieme a qualunque metamodello basato su MOF. Inoltre, OCL è un elemento chiave del nuovo standard per la trasformazione di modelli di OMG, QVT, e fa parte della famiglia di standard che compongono la Model Driven Architecture. I principali ideatori di OCL sono Jos Warmer e Anneke Kleppe.

OCL deriva da un linguaggio precedente noto come Syntropy. Il nucleo di OCL può essere descritto come un linguaggio mutuato dal calcolo dei predicati del primo ordine per l'espressione di condizioni logiche inerenti allo stato e alle operazioni di oggetti in un contesto object-oriented. Con la potenza del calcolo dei predicati, OCL consente di descrivere invarianti che legano il valore degli attributi di una classe, precondizioni e postcondizioni delle operazioni, e via dicendo. A partire dalla versione 2.0 (che è quella inclusa nello standard UML), il linguaggio è stato arricchito di elementi che consentono di descrivere la semantica di operazioni di tipo interrogazione (query), ovvero prive di effetti collaterali.

Gran parte delle informazioni che si possono descrivere in OCL non sono esprimibili in nessun altro modo formale nel contesto di UML (ovvero non possono essere rappresentate dai diagrammi UML).

Invarianti semplici

[modifica | modifica wikitesto]

In genere, in UML, un'espressione OCL è associata a una classe, e descrive proprietà degli oggetti istanze di quella classe. Queste proprietà sono espresse in forma di condizioni che legano i valori degli attributi, dei parametri, dei valori restituiti dalle operazioni, e così via. Nel seguente esempio compare un'espressione che fa riferimento a un attributo:

context Persona inv:
età>=0

La parola chiave context precede la dichiarazione della classe a cui questa regola OCL si applica. inv: specifica che questa particolare regola è un'invariante, ovvero deve essere interpretata come una condizione che è sempre vera, per tutti gli oggetti di classe Persona. L'espressione che segue dichiara che l'età di una persona è sempre non negativa.

Nel caso in cui un attributo di una classe sia a sua volta un oggetto, è possibile riferirsi ai suoi attributi o metodi utilizzando la dot notation:

context Persona inv:
età>=0 and età<padre.età

Questa regola arricchisce la precedente con un ulteriore vincolo (legato da un "AND" booleano), che specifica, inoltre, che ogni persona è più giovane del proprio padre.

Invarianti su collezioni

[modifica | modifica wikitesto]

Gli attributi con molteplicità maggiore di 1 (ovvero che rappresentano insiemi), detti collezioni, possono essere manipolati in OCL con un insieme di operatori specifici. Anche un attributo con molteplicità 1 ma trattato come collezione può essere manipolato con i seguenti operatori.

L'operatore ->size() fornisce la numerosità di una collezione. La seguente regola indica che ogni persona ha due genitori:

context Persona inv:
genitori->size()=2

L'operatore ->forAll corrisponde al quantificatore universale del calcolo dei predicati del primo ordine. La seguente regola rappresenta una sintesi delle precedenti, che specifica che ogni persona ha due genitori ed è più giovane di entrambi:

context Persona inv:
genitori->size()=2 and genitori->forAll(genitore:Persona | età<genitore.età)

Il quantificatore esistenziale è rappresentato dall'operatore ->exists. Questo operatore ritorna un valore booleano (vero o falso) a seconda che nella collezione esista almeno un'istanza che soddisfa una certa condizione. La condizione viene espressa facendo riferimento agli attributi e i metodi dell'istanza da selezionare. Nel seguente esempio, si indica che deve esistere un genitore il cui sesso è maschile, e uno il cui sesso è femminile.

context Persona inv:
genitori->size()=2 and genitori->exists(sesso=m) and genitori->exists(sesso=f)

L'operatore ->select ha una sintassi analoga a ->exists; anziché produrre un valore booleano, questo operatore produce una collezione (la collezione di tutte le istanze che soddisfano la condizione specificata). Le due seguenti espressioni sono quindi equivalenti:

collezione->exists(condizione)
collezione->select(condizione)->size()>0

L'operatore ->includes torna invece un valore booleano a seconda che una certa collezione includa o meno un determinato oggetto, e l'operatore ->excludes verifica la condizione opposta. Vi sono poi operatori che rappresentano operazioni insiemistiche, come ->union (unione).

context Persona inv:
antenati=genitori->union(genitori.antenati->asSet())

Quest'ultimo vincolo mostra come OCL consenta un'applicazione insolita della dot notation della forma <collezione>.<attributo>; questa espressione ritorna l'insieme dei valori che l'attributo specificato assume nei vari elementi della collezione. Nel caso genitori.antenati si tratterebbe di un insieme di insiemi. Per ricondurlo a un insieme semplice, viene usato l'operatore ->asSet.

Precondizioni e postcondizioni

[modifica | modifica wikitesto]

OCL consente di descrivere (parzialmente o completamente) la semantica di un'operazione di una classe per mezzo di precondizioni e postcondizioni. Una precondizione è una condizione che deve essere vera immediatamente prima dell'esecuzione di un'operazione (e quindi corrisponde anche a una guardia in un diagramma degli stati). Una postcondizione è una condizione che deve essere vera al termine dell'esecuzione di un'operazione. Precondizioni e postcondizioni sono esprimibili formalmente anche in molti linguaggi di programmazione, e possono avere sia funzioni di debug che concorrere alla semantica del programma: vedi asserzioni in Java.

context Persona::sposa(p:Persona) pre:
coniuge->size()=0
context Persona::sposa(p:Persona) post:
coniuge=p and p.coniuge=self

Nell'indicazione del contesto di questi due vincoli viene indicata una specifica operazione della classe Persona, per la quale si indica che una precondizione (introdotta dalla parola chiave pre) per l'operazione di sposare una persona p è non essere sposati (la "numerosità" dell'attributo coniuge dev'essere 0; in molti linguaggi questo si esprimerebbe dicendo che coniuge è null). La postcondizione (post è che p diventerà il mio coniuge e io (identificato dalla parola chiave self, che corrisponde al this di molti linguaggi) sono il coniuge della persona che ho sposato.

Nelle espressioni per le postcondizioni si possono usare alcuni simboli speciali dedicati. La parola chiave result indica il valore tornato dall'operazione. Il simbolo @pre, applicato al nome di un attributo, si riferisce al valore che l'attributo aveva prima che fosse eseguita l'operazione.

Regole di derivazione e query

[modifica | modifica wikitesto]

Un'espressione OCL può essere usata anche per descrivere il valore di un attributo derivato di una classe. Per esempio, le seguenti regole specificano chi sono rispettivamente i suoceri e i consuoceri di una persona:

context Persona::suoceri:Set(Persona) derive:
coniuge.genitori
context Persona::consuoceri:Set(Persona) derive:
figli.coniuge->asSet().genitori->asSet()

Estremamente simile a un attributo derivato è una query, ovvero un'operazione senza effetti collaterali che fornisce un'informazione sull'oggetto a cui viene applicata. (Da un punto di vista implementativo potrebbe non esistere alcuna distinzione). La sintassi OCL per descrivere la semantica di un'operazione query è dunque molto simile a quella per le regole di derivazione. Se suoceri fosse stata espressa come query, la si sarebbe specificata in OCL come segue:

context Persona::suoceri():Set(Persona) body:
coniuge.genitori

Valori iniziali di attributi

[modifica | modifica wikitesto]

Un'espressione OCL può essere usata anche per descrivere il valore iniziale di un attributo:

context Persona::età:Integer init:
0

Informazioni aggiuntive

[modifica | modifica wikitesto]

I vincoli OCL possono essere corredati di altre caratteristiche che non concorrono alla loro semantica ma contribuiscono alla loro leggibilità. I commenti sono introdotti dai caratteri --, e si intendono proseguire fino alla fine della linea. I vincoli possono essere dotati di un nome, composto da una singola parola (senza spazi) posta dopo la parola chiave inv, pre ecc:

context Persona::sposa(p:Persona) pre monogamia:
coniuge->size()=0

Tipi e operatori predefiniti

[modifica | modifica wikitesto]

Le espressioni OCL possono contenere riferimenti a quattro tipi primitivi, definiti anche per UML: Integer (numeri interi), Real (numeri reali), String (stringhe), Boolean (valori booleani). Su tali tipi primitivi sono definiti un vasto assortimento di operatori.

I seguenti operatori si applicano ai tipi numerici Integer e Real:

Significato Sintassi Tipo valore tornato
uguaglianza a=b Boolean
disuguaglianza a<>b Boolean
minore a<b Boolean
maggiore a>b Boolean
minore o uguale a<=b Boolean
maggiore o uguale a>=b Boolean
somma a+b Integer o Real
sottrazione a-b Integer o Real
moltiplicazione a*b Integer o Real
divisione a/b Integer o Real
divisione intera a.div(b) Integer
modulo (resto della divisione) a.mod(b) Integer
valore assoluto a.abs Integer o Real
massimo a.max(b) Integer o Real
minimo a.min(b) Integer o Real
arrotondamento all'intero più vicino a.round Integer
arrotondamento all'intero inferiore a.floor Integer

I seguenti operatori si applicano al tipo Boolean:

Significato Sintassi Tipo valore tornato
or logico (OR) a or b Boolean
and logico (AND) a and b Boolean
or esclusivo (XOR) a xor b Boolean
not logico (NOT) not a Boolean
uguaglianza a = b Boolean
disuguaglianza a <> b Boolean
implicazione a implies b Boolean
alternativa (IF..THEN..ELSE) if a then b else c tipo di "b" e "c"

I seguenti operatori si applicano al tipo String:

Significato Sintassi Tipo valore tornato
concatenazione a.concat(b) String
lunghezza a.size() Integer
trasformazione in tutte maiuscole a.toUpper() String
trasformazione in tutte minuscole a.toLower() String
sottostringa (dal carattere n-esimo all'm-esimo) a.substring(n, m) String
esistenza carattere all'interno della stringa (da A a Z) a.exists(c ┃ c = [A, ..., Z]) Boolean
uguaglianza a = b Boolean
disuguaglianza a <> b Boolean

La maggior parte dei tool e degli ambienti integrati per la modellazione in UML non gestiscono ancora OCL, o lo gestiscono in modo solo parziale. Molti dei tool elencati qui di seguito non sono integrati in ambienti di modellazione UML.

Collegamenti esterni

[modifica | modifica wikitesto]