Un guide complet pour utiliser OCL dans UML avec des exemples PlantUML
Introduction

Alors que UML (langage de modélisation unifié) est exceptionnel pour visualiser le structure d’un système, il manque de précision pour exprimer des règles métier et sémantiques. C’est là que le Langage de contrainte des objets (OCL) intervient. OCL agit comme le « ciment » formel qui lie les diagrammes UML à une logique métier stricte et inambiguë.
Ce guide explique comment OCL s’intègre à UML, les concepts clés que vous devez connaître, et fournit des exemples pratiques PlantUML exemples pour visualiser ces contraintes.
1. La synergie : comment OCL complète UML
Imaginez UML comme le « squelette » de votre système (classes, associations, états) et OCL comme le « système nerveux » (les règles, conditions et logique qui régissent le comportement du squelette).
Représentation visuelle dans UML
Dans UML standard, les contraintes OCL sont généralement représentées à l’aide de Notes attachées aux éléments de modèle pertinents (classes, opérations ou états).
-
Stéréotype :Souvent, les notes contenant de l’OCL reçoivent le
{contrainte}stéréotype. -
Contexte : Toute expression OCL commence par un
contextemot-clé, déclarant quel élément UML la règle s’applique.
2. Concepts clés de l’OCL dans UML
A. Invariants de classe (inv)
Les invariants sont des conditions qui doivent toujours être vraies pour chaque instance d’une classe, indépendamment des opérations appelées.
Concept clé : Si un invariant est violé, le système se trouve dans un état invalide.

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #FFF9C4
class BankAccount {
- accountNumber: String
- balance: Real
- isOverdraftEnabled: Boolean
+ withdraw(amount: Real)
+ deposit(amount: Real)
}
note right of BankAccount
**Invariants de classe :**
context BankAccount
-- Le solde ne peut jamais être négatif sauf si le découvert est autorisé
inv balanceRule:
self.balance >= 0 ou self.isOverdraftEnabled
-- La limite de découvert ne peut pas dépasser 500
inv overdraftLimit:
self.isOverdraftEnabled implique self.balance >= -500
end note
@enduml
B. Contrats d’opération (pré, post, corps)
Les opérations (méthodes) peuvent avoir des contraintes qui définissent leur comportement avant et après l’exécution.
-
Pré-condition (
pré): Ce qui doit être vrai avant l’opération commence. (Responsabilité de l’appelant). -
Post-condition (
post): Ce qui sera vrai après l’opération se termine. (Garantie de l’opération). -
Corps (
corps): Définit la valeur de retour exacte pour les opérations de requête.

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #E8F5E9
class PanierAchats {
- articles: Bag<Produit>
- montantTotal: Réel
+ effectuerPaiement(): Reçu
+ getMontantTotal(): Réel
}
note right of PanierAchats
**Contrats d'opération :**
-- Pré-condition : Le panier ne doit pas être vide
context PanierAchats::effectuerPaiement(): Reçu
pre: self.articles->notEmpty()
-- Post-condition : Le panier est vidé, un reçu est généré
post: self.articles->isEmpty() and result <> null
-- Corps : Définition de l'opération de requête
context PanierAchats::getMontantTotal(): Réel
corps: self.articles->collect(prix)->sum()
end note
@enduml
C. Valeurs dérivées et initiales (dérivé, init)
Au lieu de stocker des données redondantes, UML permet de marquer les attributs comme étant{dérivé} ou {initial}, avec OCL qui définit leur valeur exacte.

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #E3F2FD
class Employee {
- firstName: String
- birthDate: Date
- {derived} age: Integer
- {initial} isActive: Boolean = true
}
note right of Employee
**Valeurs dérivées et initiales :**
-- Calculer l'âge de manière dynamique
context Employee::age: Integer
derive: self.birthDate.yearsSinceToday()
-- État initial lors de la création
context Employee::isActive: Boolean
init: true
end note
@enduml
D. Gardes des machines d’état
Dans les diagrammes de machines d’état, les transitions peuvent avoirdes gardes—des expressions booléennes qui doivent évaluer à vrai pour que la transition ait lieu. OCL est le langage standard pour écrire ces gardes.

@startuml
skinparam stateBackgroundColor #F3E5F5
[*] --> Created
Created --> PendingApproval : submit()
state PendingApproval {
}
PendingApproval --> Approved : [total < 10000]
PendingApproval --> Rejected : [total >= 10000]
Approved --> Shipped : ship()
Shipped --> [*]
note right of PendingApproval
**Invariance d'état :**
context Order
inv: self.status = 'Pending' implique
self.submittedBy->notEmpty()
end note
note right of Approved
**Post-condition de approve() :**
context Order::approve()
post: self.status = 'Approved' et
self.approvedDate <> null
end note
@enduml
3. Étude de cas complète : le modèle de l’entreprise
Appliquons OCL à un diagramme de classes UML complexe impliquantEmployee, Department, etProject. Cela montre comment OCL navigue à travers les associations et utilise les itérateurs de collection (select, forAll, size).

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #FFFDE7
skinparam noteBorderColor #FBC02D
class Employee {
- SSN: String
- firstName: String
- birthDate: Date
- hireDate: Date
- salary: Float
+ age(): Integer
}
class Department {
- number: Integer
- name: String
- locations: Set<String>
- {derived} nbrEmployees: Integer
}
class Project {
- number: Integer
- name: String
- location: String
}
Employee "0..*" -- "1" Department : worksFor > department
Employee "0..*" -- "0..*" Project : worksOn > project
Employee "1" -- "0..1" Department : manages > managedDept
Department "1" -- "0..*" Project : controls > projects
Employee "1" -- "0..*" Employee : supervisor > subordinates
note right of Employee
**Invariants complexes et navigation :**
context Employee
-- 1. Règle de base sur les attributs
inv ageCheck: self.age() >= 18
-- 2. Navigation dans les associations (multiplicité)
inv maxProjects: self.worksOn->size() <= 4
-- 3. Utilisation des itérateurs (forAll)
-- Le superviseur doit être embauché avant ses subordonnés
inv hireOrder: self.subordinates->forAll(
sub | sub.hireDate > self.hireDate
)
-- 4. Règle croisée entre associations
-- Un employé ne peut travailler que sur des projets contrôlés par son département
inv projectControl:
self.worksFor.controls->includesAll(self.worksOn)
-- 5. Identifiant unique (clé)
inv uniqueSSN: Employee.allInstances()->forAll(e1, e2 |
e1 <> e2 implique e1.SSN <> e2.SSN
)
end note
note bottom of Department
**Attribut dérivé :**
context Department::nbrEmployees: Integer
derive: self.worksFor->size()
end note
note right of Project
**Invariance du projet :**
context Project
-- La localisation du projet doit être l'une des localisations du département
inv validLocation:
self.controls.locations->includes(self.location)
end note
@enduml
4. Techniques avancées OCL dans UML
A. Utilisation delaisser pour la lisibilité
Lorsqu’une expression OCL devient trop complexe, utilisez le laissermot-clé pour définir des variables locales dans la contrainte. Cela rend la note UML beaucoup plus facile à lire.

@startuml
skinparam classAttributeIconSize 0
class Employee {
- worksOn: Set<Project>
}
note right of Employee
**Utilisation de 'let' pour une logique complexe :**
context Employee
-- Calculer le nombre total d'heures et vérifier les limites
inv workingHours:
laisser totalHours: Integer =
self.worksOn->collect(hours)->sum()
dans totalHours >= 30 et totalHours <= 50
end note
@enduml
B. Gestion des valeurs précédentes dans les post-conditions (@pre)
Lors de la définition d’une post-condition, vous devez souvent comparer l’état nouveau avec l’état ancien état. OCL utilise le @pre suffixe pour accéder à la valeur d’une propriété exactement comme elle était avant le début de l’opération.

@startuml
skinparam classAttributeIconSize 0
class Company {
- employees: Set<Employee>
+ hireEmployee(p: Employee)
}
note right of Company
**Utilisation de @pre dans les post-conditions :**
context Company::hireEmployee(p: Employee)
-- L'ensemble des nouveaux employés est l'ensemble ancien + la nouvelle personne
post: self.employees = self.employees@pre->including(p)
end note
@enduml
5. Meilleures pratiques pour OCL dans UML
-
Nommez vos contraintes : Donnez toujours un nom à vos invariants et conditions (par exemple,
inv maxProjects:). Cela facilite leur référence dans la documentation ou les matrices de traçabilité. -
Utilisez les notes de manière stratégique : Dans PlantUML et les outils UML visuels, n’encombrez pas la boîte de classe avec OCL. Placez OCL dans des notes adjacentes. Cela maintient le diagramme propre et lisible.
-
Ne pas trop contraindre : Écrivez uniquement du OCL pour les règles qui ne peuvent pas être facilement exprimées via les multiplicités UML (par exemple,
0..1,1..*) ou les types de base. Si une règle peut être montrée visuellement, montrez-la visuellement. -
Utilisez les itérateurs de collection : Maîtrisez
->select(),->forAll(),->exists(), et->collect(). Ce sont les outils les plus puissants pour naviguer dans les associations UML en OCL. -
Vérifiez les valeurs nulles/ensembles vides : Lorsque vous naviguez dans des associations optionnelles (multiplicité
0..1ou0..*), utilisez toujours->notEmpty()ou->isEmpty()avant d’accéder aux propriétés afin d’éviter les erreurs d’évaluation non définies.














