Um Guia Completo para o Uso do OCL no UML com Exemplos em PlantUML
Introdução

Embora o UML (Linguagem de Modelagem Unificada) seja excepcional para visualizar o estrutura de um sistema, ele carece da precisão para expressar complexos regras de negócios e semânticas. É aqui que o Linguagem de Restrição de Objetos (OCL) entra em ação. OCL atua como a “cola” formal que liga os diagramas UML a lógica de negócios rigorosa e inequívoca.
Este guia explica como o OCL se integra ao UML, os conceitos-chave que você precisa conhecer e fornece exemplos práticos PlantUML exemplos para visualizar essas restrições.
1. A Sinergia: Como o OCL Complementa o UML
Pense no UML como o “esqueleto” do seu sistema (classes, associações, estados) e no OCL como o “sistema nervoso” (as regras, condições e lógica que regem como o esqueleto se comporta).
Representação Visual no UML
No UML padrão, as restrições OCL são geralmente representadas usando Notas anexadas aos elementos do modelo relevantes (classes, operações ou estados).
-
Estereótipo: Freqüentemente, notas contendo OCL são dadas o
{restrição}estereótipo. -
Contexto: Toda expressão OCL começa com um
contextopalavra-chave, declarando qual elemento UML a regra se aplica.
2. Conceitos-chave do OCL no UML
A. Invariantes de Classe (inv)
Invariantes são condições que devem sempre ser verdadeiras para cada instância de uma classe, independentemente das operações chamadas.
Conceito-chave: Se um invariante for violado, o sistema estará em um estado inválido.

@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
**Invariantes de Classe:**
context BankAccount
-- O saldo nunca pode ser negativo, a menos que o cheque especial esteja habilitado
inv balanceRule:
self.balance >= 0 ou self.isOverdraftEnabled
-- O limite de cheque especial não pode exceder 500
inv overdraftLimit:
self.isOverdraftEnabled implica self.balance >= -500
end note
@enduml
B. Contratos de Operação (pré, pós, corpo)
Operações (métodos) podem ter restrições que definem seu comportamento antes e após a execução.
-
Pré-condição (
pré):O que deve ser verdadeiroantesa operação começar. (Responsabilidade do chamador). -
Pós-condição (
pós):O que será verdadeirodepoisa operação terminar. (Garantia da operação). -
Corpo (
corpo):Define o valor exato de retorno para operações de consulta.

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #E8F5E9
class ShoppingCart {
- items: Bag<Product>
- totalAmount: Real
+ checkout(): Receipt
+ getTotal(): Real
}
note right of ShoppingCart
**Contratos de Operação:**
-- Pré-condição: O carrinho não deve estar vazio
context ShoppingCart::checkout(): Receipt
pre: self.items->notEmpty()
-- Pós-condição: O carrinho é esvaziado, o comprovante é gerado
post: self.items->isEmpty() and result <> null
-- Corpo: Definição da operação de consulta
context ShoppingCart::getTotal(): Real
body: self.items->collect(price)->sum()
end note
@enduml
C. Valores Derivados e Iniciais (derivar, inicializar)
Em vez de armazenar dados redundantes, o UML permite que atributos sejam marcados como{derivado}ou{inicial}, com o OCL definindo seu valor exato.

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #E3F2FD
class Employee {
- firstName: String
- birthDate: Date
- {derived} age: Integer
- {initial} isActive: Boolean = true
}
note right of Employee
**Valores Derivados e Iniciais:**
-- Calcular idade dinamicamente
context Employee::age: Integer
derive: self.birthDate.yearsSinceToday()
-- Estado inicial na criação
context Employee::isActive: Boolean
init: true
end note
@enduml
D. Guardas de Máquina de Estados
Nos diagramas de máquina de estados, as transições podem ter guardas—expressões booleanas que devem avaliar-se como verdadeiras para que a transição ocorra. OCL é a linguagem padrão para escrever essas guardas.

@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
**Invariante de Estado:**
context Order
inv: self.status = 'Pending' implica
self.submittedBy->notEmpty()
end note
note right of Approved
**Pós-condição de approve():**
context Order::approve()
post: self.status = 'Approved' e
self.approvedDate <> null
end note
@enduml
3. Estudo de Caso Compreensivo: O Modelo da Empresa
Vamos aplicar o OCL a um diagrama de classes UML complexo envolvendo Employee, Department, e Project. Isso demonstra como o OCL navega por associações e usa iteradores de coleção (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
**Invariantes Complexos e Navegação:**
context Employee
-- 1. Regra Básica de Atributo
inv ageCheck: self.age() >= 18
-- 2. Navegação por Associações (Multiplicidade)
inv maxProjects: self.worksOn->size() <= 4
-- 3. Uso de Iteradores (forAll)
-- O supervisor deve ser contratado antes dos subordinados
inv hireOrder: self.subordinates->forAll(
sub | sub.hireDate > self.hireDate
)
-- 4. Regra de Associação Cruzada
-- O funcionário só pode trabalhar em projetos controlados pelo seu departamento
inv projectControl:
self.worksFor.controls->includesAll(self.worksOn)
-- 5. Identificador Único (Chave)
inv uniqueSSN: Employee.allInstances()->forAll(e1, e2 |
e1 <> e2 implica e1.SSN <> e2.SSN
)
end note
note bottom of Department
**Atributo Derivado:**
context Department::nbrEmployees: Integer
derive: self.worksFor->size()
end note
note right of Project
**Invariante do Projeto:**
context Project
-- A localização do projeto deve ser uma das localizações do departamento
inv validLocation:
self.controls.locations->includes(self.location)
end note
@enduml
4. Técnicas Avançadas de OCL em UML
A. Usando let para legibilidade
Quando uma expressão OCL torna-se muito complexa, use o letpalavra-chave para definir variáveis locais dentro da restrição. Isso torna a nota UML muito mais fácil de ler.

@startuml
skinparam classAttributeIconSize 0
class Employee {
- worksOn: Set<Project>
}
note right of Employee
**Usando 'let' para lógica complexa:**
context Employee
-- Calcular horas totais e verificar limites
inv workingHours:
let totalHours: Integer =
self.worksOn->collect(hours)->sum()
in totalHours >= 30 e totalHours <= 50
end note
@enduml
B. Manipulando valores anteriores em pós-condições (@pre)
Ao definir uma pós-condição, você frequentemente precisa comparar o novo estado com o antigo estado. OCL usa o @pre sufixo para acessar o valor de uma propriedade exatamente como era antes da operação começar.

@startuml
skinparam classAttributeIconSize 0
class Company {
- employees: Set<Employee>
+ hireEmployee(p: Employee)
}
note right of Company
**Usando @pre em pós-condições:**
context Company::hireEmployee(p: Employee)
-- O novo conjunto de funcionários é o conjunto antigo + a nova pessoa
post: self.employees = self.employees@pre->including(p)
end note
@enduml
5. Melhores práticas para OCL em UML
-
Nomeie suas restrições: Dê sempre um nome às suas invariantes e condições (por exemplo,
inv maxProjects:). Isso torna mais fácil referenciá-las na documentação ou em matrizes de rastreabilidade. -
Use notas de forma estratégica: No PlantUML e em ferramentas visuais de UML, não encha a caixa da classe com OCL. Coloque o OCL em notas adjacentes. Isso mantém o diagrama limpo e legível.
-
Não sobrecarregue: Escreva apenas OCL para regras que não podem ser facilmente expressas por meio de multiplicidades UML (por exemplo,
0..1,1..*) ou tipos básicos. Se uma regra puder ser mostrada visualmente, mostre-a visualmente. -
Aproveite os iteradores de coleção: Domine
->select(),->forAll(),->exists(), e->collect(). São as ferramentas mais poderosas para navegar associações UML em OCL. -
Verifique nulos/conjuntos vazios: Ao navegar associações opcionais (multiplicidade
0..1ou0..*), sempre use->notEmpty()ou->isEmpty()antes de acessar propriedades para evitar erros de avaliação indefinida.














