Ein umfassender Leitfaden zum Einsatz von OCL in UML mit PlantUML-Beispielen
Einführung

Während UML (Unified Modeling Language) hervorragend geeignet ist, die Struktureines Systems zu visualisieren, fehlt es ihr an der Präzision, komplexe Geschäftsregeln und Semantikauszudrücken. Hier kommt die Object Constraint Language (OCL)zum Einsatz. OCL fungiert als formales „Klebeband“, das UML-Diagramme mit strenger, eindeutiger Geschäftslogik verbindet.
Dieser Leitfaden erklärt, wie OCL mit UML integriert wird, die zentralen Konzepte, die Sie kennen müssen, und liefert praktische PlantUMLBeispiele, um diese Beschränkungen zu visualisieren.
1. Die Synergie: Wie OCL UML ergänzt
Stellen Sie sich UML als das „Skelett“ Ihres Systems (Klassen, Assoziationen, Zustände) und OCL als das „Nervensystem“ (die Regeln, Bedingungen und Logik, die bestimmen, wie das Skelett sich verhält) vor.
Visuelle Darstellung in UML
In der Standard-UML werden OCL-Beschränkungen typischerweise mithilfe von Notizenan die relevanten Modell-Elemente (Klassen, Operationen oder Zustände) angehängt.
-
Stereotyp: Häufig erhalten Notizen, die OCL enthalten, das
{Einschränkung}Stereotyp. -
Kontext: Jeder OCL-Ausdruck beginnt mit einem
KontextSchlüsselwort, das angibt, auf welches UML-Element die Regel anwendbar ist.
2. Hauptkonzepte von OCL in UML
A. Klasseninvarianten (inv)
Invarianzen sind Bedingungen, die immer wahr sein müssen für jede Instanz einer Klasse gelten müssen, unabhängig davon, welche Operationen aufgerufen werden.
Wichtiges Konzept: Wenn eine Invariante verletzt wird, befindet sich das System in einem ungültigen Zustand.

@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
**Klasseninvarianten:**
context BankAccount
-- Der Kontostand kann niemals negativ sein, es sei denn, die Überziehung ist aktiviert
inv balanceRule:
self.balance >= 0 oder self.isOverdraftEnabled
-- Die Überziehungsgrenze darf 500 nicht überschreiten
inv overdraftLimit:
self.isOverdraftEnabled impliziert self.balance >= -500
end note
@enduml
B. Operationsverträge (pre, post, body)
Operationen (Methoden) können Einschränkungen haben, die ihr Verhalten vor und nach der Ausführung definieren.
-
Vorbedingung (
pre): Was wahr sein muss vor der Operation beginnt. (Verantwortung des Aufrufers). -
Nachbedingung (
post): Was wahr sein wird nach der Operation beendet ist. (Zusage der Operation). -
Körper (
Körper): Definiert den genauen Rückgabewert für Abfrageoperationen.

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #E8F5E9
class ShoppingCart {
- items: Bag<Product>
- totalAmount: Real
+ checkout(): Receipt
+ getTotal(): Real
}
note right of ShoppingCart
**Operationsverträge:**
-- Vorbedingung: Der Warenkorb darf nicht leer sein
context ShoppingCart::checkout(): Receipt
pre: self.items->notEmpty()
-- Nachbedingung: Der Warenkorb wird geleert, Beleg wird generiert
post: self.items->isEmpty() und result <> null
-- Körper: Definition der Abfrageoperation
context ShoppingCart::getTotal(): Real
body: self.items->collect(price)->sum()
end note
@enduml
C. Abgeleitete und anfängliche Werte (ableiten, init)
Anstelle von redundanten Daten zu speichern, ermöglicht UML, Attribute als {abgeleitet} oder {anfänglich}, wobei OCL ihren genauen Wert definiert.

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #E3F2FD
class Employee {
- firstName: String
- birthDate: Date
- {abgeleitet} age: Integer
- {initial} isActive: Boolean = true
}
note right of Employee
**Abgeleitete & Anfangswerte:**
-- Berechne Alter dynamisch
context Employee::age: Integer
ableiten: self.birthDate.yearsSinceToday()
-- Anfangszustand bei Erstellung
context Employee::isActive: Boolean
init: true
end note
@enduml
D. Zustandsmaschinen-Guards
In Zustandsmaschinen-Diagrammen können Übergänge Guards—boolesche Ausdrücke, die wahr sein müssen, damit der Übergang erfolgen kann. OCL ist die Standard-Sprache zum Schreiben dieser Guards.

@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
**Zustandsinvariante:**
context Order
inv: self.status = 'Pending' impliziert
self.submittedBy->notEmpty()
end note
note right of Approved
**Nachbedingung von approve():**
context Order::approve()
post: self.status = 'Approved' und
self.approvedDate <> null
end note
@enduml
3. Umfassender Fallstudien-Beispiel: Das Unternehmensmodell
Lassen Sie uns OCL auf ein komplexes UML-Klassendiagramm anwenden, das folgendes beinhaltet Mitarbeiter, Abteilung, und Projekt. Dies zeigt, wie OCL Assoziationen durchläuft und Sammlungs-Iteratoren verwendet (select, forAll, size).

@startuml
skinparam classAttributeIconSize 0
skinparam noteBackgroundColor #FFFDE7
skinparam noteBorderColor #FBC02D
class Mitarbeiter {
- SSN: String
- firstName: String
- birthDate: Date
- hireDate: Date
- salary: Float
+ age(): Integer
}
class Abteilung {
- number: Integer
- name: String
- locations: Set<String>
- {abgeleitet} nbrEmployees: Integer
}
class Projekt {
- number: Integer
- name: String
- location: String
}
Mitarbeiter "0..*" -- "1" Abteilung : worksFor > department
Mitarbeiter "0..*" -- "0..*" Projekt : worksOn > project
Mitarbeiter "1" -- "0..1" Abteilung : manages > managedDept
Abteilung "1" -- "0..*" Projekt : controls > projects
Mitarbeiter "1" -- "0..*" Mitarbeiter : supervisor > subordinates
note right of Mitarbeiter
**Komplexe Invarianten & Navigation:**
context Mitarbeiter
-- 1. Grundregel für Attribute
inv ageCheck: self.age() >= 18
-- 2. Navigieren von Assoziationen (Mehrfachheit)
inv maxProjects: self.worksOn->size() <= 4
-- 3. Verwenden von Iteratoren (forAll)
-- Supervisor muss vor seinen Untergebenen eingestellt werden
inv hireOrder: self.subordinates->forAll(
sub | sub.hireDate > self.hireDate
)
-- 4. Regel über Assoziationen hinweg
-- Mitarbeiter kann nur an Projekten arbeiten, die von seiner Abteilung kontrolliert werden
inv projectControl:
self.worksFor.controls->includesAll(self.worksOn)
-- 5. Eindeutiger Bezeichner (Schlüssel)
inv uniqueSSN: Mitarbeiter.allInstances()->forAll(e1, e2 |
e1 <> e2 impliziert e1.SSN <> e2.SSN
)
end note
note bottom of Abteilung
**Abgeleitetes Attribut:**
context Abteilung::nbrEmployees: Integer
ableiten: self.worksFor->size()
end note
note right of Projekt
**Projektinvariante:**
context Projekt
-- Projektstandort muss einer der Standorte der Abteilung sein
inv validLocation:
self.controls.locations->includes(self.location)
end note
@enduml
4. Fortgeschrittene OCL-Techniken in UML
A. Verwenden von let zur Lesbarkeit
Wenn ein OCL-Ausdruck zu komplex wird, verwenden Sie die letSchlüsselwort, um lokale Variablen innerhalb der Einschränkung zu definieren. Dadurch wird die UML-Notiz viel leichter lesbar.

@startuml
skinparam classAttributeIconSize 0
class Employee {
- worksOn: Set<Project>
}
note right of Employee
**Verwendung von 'let' für komplexe Logik:**
context Employee
-- Gesamte Stunden berechnen und Grenzen überprüfen
inv workingHours:
let totalHours: Integer =
self.worksOn->collect(hours)->sum()
in totalHours >= 30 und totalHours <= 50
end note
@enduml
B. Behandlung vorheriger Werte in Postbedingungen (@pre)
Beim Definieren einer Postbedingung müssen Sie oft den neuen Zustand mit dem alten Zustand vergleichen. OCL verwendet das @pre Postfix, um den Wert einer Eigenschaft genau so zu erhalten, wie er war, bevor die Operation begann.

@startuml
skinparam classAttributeIconSize 0
class Company {
- employees: Set<Employee>
+ hireEmployee(p: Employee)
}
note right of Company
**Verwendung von @pre in Postbedingungen:**
context Company::hireEmployee(p: Employee)
-- Die neue Mitarbeitermenge ist die alte Menge plus die neue Person
post: self.employees = self.employees@pre->including(p)
end note
@enduml
5. Best Practices für OCL in UML
-
Benennen Sie Ihre Einschränkungen: Geben Sie Ihren Invarianten und Bedingungen immer einen Namen (z. B.
inv maxProjects:). Dadurch wird es einfacher, sie in Dokumentationen oder Nachverfolgungsmatrizen zu referenzieren. -
Verwenden Sie Notizen strategisch: Verwenden Sie in PlantUML und visuellen UML-Tools keine überladenen Klassenboxen mit OCL. Stellen Sie den OCL in benachbarte Notizen. Dadurch bleibt das Diagramm übersichtlich und lesbar.
-
Überbeanspruche nicht: Schreibe OCL nur für Regeln, die nicht leicht über UML-Multiplicitäten (z. B.
0..1,1..*) oder einfache Typen ausgedrückt werden können. Wenn eine Regel visuell dargestellt werden kann, zeige sie visuell. -
Nutze Sammlungs-Iteratoren: Beherrsche
->select(),->forAll(),->exists(), und->collect(). Sie sind die mächtigsten Werkzeuge zum Navigieren von UML-Assoziationen in OCL. -
Prüfe auf Nullwerte/leere Mengen: Beim Navigieren optionaler Assoziationen (Multiplicität
0..1oder0..*), verwende immer->notEmpty()oder->isEmpty()vor dem Zugriff auf Eigenschaften, um undefinierte Auswertungsfehler zu vermeiden.














