Object Constraint Language
# Object Constraint Language

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).

## Descrizione

### Invarianti semplici

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

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

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

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

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

    context Persona::età:Integer init:
    0

### Informazioni aggiuntive

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

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 |

## Tool

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.

* Model Run (Borland,), attualmente incluso in Borland Delphi 7 Studio Architect
* OCL Compiler (Cybernetic Intelligence GMBH, ), un analizzatore di espressioni OCL, con diversi front-end per l'integrazione in ambienti UML come Rational Rose
* OCLCUD (Università di Dresda), ), integrato con ArgoUML
* Octopus (Klasse Objecten, Archiviato l'8 febbraio 2005 in Internet Archive.). Supporta completamente l'ultima versione di OCL, 2.0. Creato dagli autori di OCL.

## Curiosità

* Il linguaggio Eiffel fu uno dei primi linguaggi object-oriented a includere un sottolinguaggio dichiarativo per esprimere asserzioni
* Anche Java ha un proprio sottolinguaggio per le asserzioni
* Syntropy, Catalysis e BON sono altri linguaggi che consentono di esprimere vincoli su sistemi a oggetti
* L'uso di precondizioni, postcondizioni e invarianti come strumenti di specifica di dati e operazioni ha origine negli studi sulla semantica assiomatica di C.A.R. Hoare e Edsger Dijkstra.
