Dankwoord - martes
Dankwoord - martes
Dankwoord - martes
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Dankwoord</strong><br />
Ik wil alle mensen bedanken die rechtstreeks of onrechtstreeks hebben meegewerkt aan het tot<br />
stand brengen van deze thesis.<br />
In de eerste plaats gaat mijn dank naar mijn ouders die deze studies mogelijk gemaakt hebben.<br />
Daarnaast wil ik in het bijzonder mijn promotoren prof. dr. ir. Wouter Joosen en prof. dr.<br />
ir. Yolande Berbers en begeleiders Aram Hovsepyan en dr. Stefan Van Baelen bedanken. Ik<br />
mocht wekelijks langskomen om de evolutie van mijn thesis te bespreken. Aram en Stefan<br />
moedigden me aan waar nodig en stonden steeds klaar met goede raad. Ze zorgden ook voor<br />
het grondig nalezen van deze thesistekst.<br />
Ook wil ik Guy Pauwels en Michel Huybrechts van E2S bedanken.<br />
Nog vele anderen uit mijn omgeving verdienen mijn dank - mijn vrienden en vooral mijn<br />
vriendin voor de steun, interesse, hulp en suggesties die ze geboden heeft bij het maken van<br />
dit eindwerk.<br />
i
Inhoudsopgave<br />
1 Inleiding 1<br />
1.1 Situering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1<br />
1.2 Probleem beschrijving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3<br />
1.2.1 Wat is OCL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3<br />
1.3 Motivatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4<br />
1.4 Doelstellingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />
1.5 Overzicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />
2 State-of-the-art 7<br />
2.1 Evaluatiecriteria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />
2.1.1 Invarianten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />
2.1.2 Pre- en Postcondities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />
2.1.3 Attributen en Associaties . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />
2.1.4 Operaties op collecties . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />
2.1.5 @pre en result . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8<br />
2.1.6 Assertion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9<br />
2.1.7 Slimme controle van klasse-invarianten . . . . . . . . . . . . . . . . . . . 9<br />
2.2 Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />
2.2.1 Octopus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />
2.2.2 OCLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11<br />
2.2.3 Together 2006 for Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />
2.2.4 Conclusie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16<br />
ii
INHOUDSOPGAVE<br />
iii<br />
3 Methodologie 17<br />
3.1 Design By Contract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17<br />
3.1.1 Klasse-invarianten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />
3.1.2 Precondities en postcondities . . . . . . . . . . . . . . . . . . . . . . . . 18<br />
3.1.3 Assertions in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19<br />
3.1.3.1 Waarom assertie gebruiken? . . . . . . . . . . . . . . . . . . . . 19<br />
3.2 Beschrijving van de methodologie . . . . . . . . . . . . . . . . . . . . . . . . . . 20<br />
3.2.1 Vertaling van attributen en associaties . . . . . . . . . . . . . . . . . . . 20<br />
3.2.2 Vertaling van OCL operaties over basistypen . . . . . . . . . . . . . . . 21<br />
3.2.2.1 Boolean type . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21<br />
3.2.2.2 Real en Integer types . . . . . . . . . . . . . . . . . . . . . . . 22<br />
3.2.2.3 String type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />
3.2.3 Vertaling van OCL collectieoperaties . . . . . . . . . . . . . . . . . . . . 23<br />
3.2.3.1 Eenvoudige collectieoperaties . . . . . . . . . . . . . . . . . . . 24<br />
3.2.3.2 Collectieiteratoren . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.2.3.3 Inkapseling van sjablonen voor collectieoperaties in JAVA methoden<br />
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31<br />
3.2.4 Vertaling van OCL if-then-else expressies . . . . . . . . . . . . . . . . 32<br />
3.2.5 Vertaling van OCL let expressies . . . . . . . . . . . . . . . . . . . . . . 33<br />
3.2.6 Vertaling van klasse-invarianten . . . . . . . . . . . . . . . . . . . . . . . 33<br />
3.2.7 Vertaling van pre- en postcondities . . . . . . . . . . . . . . . . . . . . . 34<br />
3.2.8 Vertaling van meer geavanceerde constructies . . . . . . . . . . . . . . . 35<br />
3.2.8.1 Vertaling van het sleutelwoord @pre . . . . . . . . . . . . . . . 35<br />
3.2.8.2 Het sleutelwoord result . . . . . . . . . . . . . . . . . . . . . 36<br />
3.3 Naïeve aanpak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />
3.3.1 Vertaling van invarianten . . . . . . . . . . . . . . . . . . . . . . . . . . 37<br />
3.3.1.1 Inkapseling van een invariant in een booleaanse methode . . . 37<br />
3.3.1.2 Inkapseling van alle klasse-invarianten in een gemeenschappelijke<br />
methode . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38<br />
3.3.2 Vertaling van pre- en postcondities . . . . . . . . . . . . . . . . . . . . . 38<br />
3.4 Intelligente aanpak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />
3.4.1 Het Idee . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />
3.4.2 Graafconstructie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />
3.4.3 String matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
iv<br />
INHOUDSOPGAVE<br />
3.4.4 Vertaling van klasse-invarianten . . . . . . . . . . . . . . . . . . . . . . . 42<br />
3.4.4.1 Inkapseling van een invariant in een booleaanse methode . . . 43<br />
3.4.4.2 Inkapseling van alle relevante klasse-invarianten in een gemeenschappelijke<br />
booleaanse methode . . . . . . . . . . . . . . . . . 43<br />
3.4.5 Vertaling van pre- en postcondities . . . . . . . . . . . . . . . . . . . . . 43<br />
3.5 Evaluatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43<br />
4 Realisatie in HAT 46<br />
4.1 HAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46<br />
4.2 Run-time OCL naar JAVA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48<br />
4.2.1 OCL attribuut-, associatie- en operatiemodelelementen . . . . . . . . . . 49<br />
4.2.2 OCL collectieiterator modelelementen . . . . . . . . . . . . . . . . . . . 49<br />
4.2.3 OCL If-then-else en let modelelementen . . . . . . . . . . . . . . . . . . 50<br />
4.2.4 Inkapseling van collectieoperaties . . . . . . . . . . . . . . . . . . . . . . 50<br />
4.3 Naïeve aanpak in HAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51<br />
4.4 Intelligente aanpak in HAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51<br />
4.5 Evaluatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />
5 Case study - Royal and Loyal 54<br />
5.1 Beschrijving van de case study . . . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />
5.2 Vertaling met behulp van HAT . . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />
6 Besluit 59<br />
6.1 Doelstellingen en problemen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59<br />
6.2 Oplossing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59<br />
6.3 Kritische noot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60<br />
6.4 Toekomstig werk: uitbreidingen en aanpassingen . . . . . . . . . . . . . . . . . 60<br />
A Lijst Van Afkortingen 62
L¼st van guren<br />
1.1 Relatie tussen PIM, PSM en code . . . . . . . . . . . . . . . . . . . . . . . . . . 2<br />
1.2 Gesimpliceerd UML model van een Luchthaven systeem . . . . . . . . . . . . . 3<br />
3.1 Afhankelijkheid tussen klasse invarianten en postcondities horend bij operaties . 41<br />
3.2 Klassediagram van het lopende voorbeeld. . . . . . . . . . . . . . . . . . . . . . 45<br />
4.1 Architectuur van de Agile MDA Tool . . . . . . . . . . . . . . . . . . . . . . . . 47<br />
4.2 Gesimpliceerde OCL meta-model . . . . . . . . . . . . . . . . . . . . . . . . . 48<br />
4.3 Abstract syntax metamodel voor ModelPropertyCallExpr . . . . . . . . . . . . 49<br />
5.1 Het aangepaste model van Royal and Loyal . . . . . . . . . . . . . . . . . . . . 55<br />
5.2 Het model van Royal and Loyal . . . . . . . . . . . . . . . . . . . . . . . . . . . 56<br />
v
L¼st van tabellen<br />
2.1 Evaluatiecriteria voor transformatie van OCL expressies door een tool . . . . . 9<br />
2.2 Evaluatie van Octopus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10<br />
2.3 Evaluatie van OCLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />
2.4 Evaluatie van Together 2006 for Eclipse . . . . . . . . . . . . . . . . . . . . . . 13<br />
3.1 Standaard operaties voor de Boolean type . . . . . . . . . . . . . . . . . . . . . 21<br />
3.2 Standaard operaties voor Integer en Real types . . . . . . . . . . . . . . . . . . 22<br />
3.3 Standaardoperaties voor String type . . . . . . . . . . . . . . . . . . . . . . . . 23<br />
3.4 Afbeelding van OCL collectie typen naar JAVA collectie typen . . . . . . . . . 23<br />
3.5 Standaardoperaties over alle soorten van collectie typen . . . . . . . . . . . . . 24<br />
3.6 Collectieiteratoren operaties over alle collectietypen . . . . . . . . . . . . . . . . 25<br />
3.7 De resultaten van de evaluatie van naïeve en de intelligente aanpak . . . . . . . 44<br />
4.1 Evaluatie van HAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53<br />
vi
Hoofdstuk 1<br />
Inleiding<br />
1.1 Situering<br />
De computer industrie is continu op zoek naar manieren om de productiviteit en kwaliteit van<br />
software ontwikkeling te verbeteren. In de vrij korte geschiedenis van software engineering<br />
hebben we al verschillende aanpakken gezien. Voorbeelden daarvan zijn Object-georiënteerdeontwikkelingen,<br />
Component-gebaseerde- ontwikkelingen, design patterns.<br />
Modelgedreven ontwikkeling (MDD) zou ook een belangrijke bijdrage in het proces van software<br />
ontwikkeling kunnen leveren. Een van de belangrijkste doelstellingen van het MDD is<br />
om het niveau van programmeerabstractie te verhogen zodat ontwikkelaars applicaties met<br />
minder moeite kunnen ontwikkelen. In plaats van applicaties in talen zoals JAVA en C++ te<br />
schrijven, creëren ontwikkelaars applicaties met behulp van specieke, hoogniveau programmeerconstructies.<br />
Dit soort constructies noemt men modellen. Deze software modellen hebben<br />
een centrale rol in het MDD ontwikkelingsproces.<br />
In MDD kan men speciceren hoe modellen gedenieerd in een taal naar modellen van een<br />
andere taal mogen getransformeerd worden. Modelleertalen worden in MDD als ontwikkelingsartefacten<br />
gebruikt veeleer dan enkel als ontwerpmiddelen. Uit een abstract model van<br />
een systeem wordt een meer concreet model gegenereerd. Uit dit model wordt dan nog een<br />
meer concreet model gegenereerd. Dit proces wordt meerdere keren uitgevoerd tot uiteindelijk<br />
het laagste niveau van abstractie bereikt is. Van deze laagniveau modellen wordt de broncode<br />
geproduceerd. Hierbij is codegeneratie niets meer dan een andere vorm van transformatie.<br />
Modelgedreven architectuur (MDA) [20] is eigenlijk een concreet voorbeeld van MDD. Het is<br />
een initiatief van Object Management Group (OMG) [16] dat een consortium van bedrijven en<br />
gebruikers is. De OMG heeft bovendien verschillende standaarden (UML, OCL, ...) ontwikkeld<br />
om MDD te kunnen implementeren.<br />
MDA is een verdere stap in de evolutie van het software ontwikkelingsproces. MDA is een<br />
raamwerk voor software ontwikkeling. Modellen krijgen een hoge prioriteit binnen het proces<br />
van software ontwikkeling. Anders gezegd wordt het ontwikkelingsproces gedreven door de<br />
modelleeractiviteit. Hierbij maakt men onderscheid tussen platformonafhankelijke modellen<br />
(PIM) en platformafhankelijke modellen (PSM). PIMs zijn op een hoogniveau van abstractie<br />
gebouwd en bovendien onafhankelijk van een set van implementatie technologieën. Voorbeelden<br />
daarvan zijn J2EE en .NET. PSMs zijn het resultaat van de transformatie van de<br />
1
2 HOOFDSTUK 1. INLEIDING<br />
PIM naar de nood voor een specieke implementatie technologie, bijvoorbeeld J2EE. Daarna<br />
wordt er vanuit de PSM code gegenereerd wat kan gezien worden als een transformatie van<br />
een PSM-model naar een code-model.<br />
Figuur 1.1: Relatie tussen PIM, PSM en code<br />
Sinds het MDA [3] proces gedreven wordt door een PIM dat automatisch getransformeerd<br />
wordt naar een PSM en nog verder naar code, kan modelleren beschouwd worden als programmeren<br />
op een hoger niveau. PIMs speciceren de code die geproduceerd (gegenereerd) moet<br />
worden. Bijgevolg zal het volume van de handgeschreven code kleiner zijn. Programmeren<br />
wordt uiteindelijk modelleren. Het software ontwikkelingsproces zal rond goede, hoog-niveau,<br />
onafhankelijke modellen draaien.<br />
Modelleren en vooral software modelleren wordt soms gebuikt als synoniem voor het maken<br />
van diagrammen. De meeste van de modellen bestaan uit een aantal vierkantjes, cirkels met<br />
pijlen en bijbehorende tekst. De informatie, die men van de gegeven modellen kan extraheren,<br />
is meestal incompleet, informeel en zelfs niet precies. De Object Constraint Language (OCL)<br />
[21] legt beperkingen op het model en op de semantiek ervan. Met OCL kan men al de supplementaire<br />
en benodigde informatie over de modellen in het model zelf uitdrukken. Speciceren<br />
van modellen gebruik makende van Unied Modeling Language (UML) [15] en de OCL-taal<br />
kan de kwaliteit van de modellen verbeteren.
1.2. PROBLEEM BESCHRIJVING 3<br />
1.2 Probleem beschrijving<br />
Net zoals broncode, moeten MDD modellen juist en precies zijn om tot een correcte broncode te<br />
kunnen komen. Er bestaan verschillende modelleertalen die in een MDD ontwikkelingsproces<br />
gebruikt kunnen worden. Voorbeelden daarvan zijn UML, GME, MetaEdit [15,17,18]. Hoe<br />
juist en precies men in elk van die talen kan modelleren hangt af van taal tot taal. In deze<br />
thesis gaan we enkel naar UML kijken. UML heeft veel zwakke punten maar blijft toch een<br />
van de meest populaire modelleertalen. Een van de zwakheden van UML modellen (vanaf<br />
hier gaan we de term modellen gebruiken in de context van UML) wordt veroorzaakt door de<br />
semantische onvolledigheid van de modellen.<br />
Modellen kunnen niet alle vaststellingen van een specicatie beschrijven. Bijvoorbeeld het<br />
model in figuur 1.2, duidt de associatie tussen de klasse Person en de klasse Flight aan<br />
dat er met een vlucht nul of meer passagiers geassocieerd kunnen zijn : multipliciteit (0..*).<br />
Dit impliceert dat het aantal passagiers oneindig kan zijn. In werkelijkheid zal dit aantal<br />
eerder beperkt zijn tot het aantal zetels in het vliegtuig dat aan de vlucht geassocieerd is.<br />
Dit voorbeeld toont aan dat deze beperking onbeschrijfbaar is in het UML klasse-diagram.<br />
Bijgevolg zullen modellen die in UML uitgedrukt zijn meestal incompleet, informeel en zelfs<br />
niet precies zijn.<br />
Figuur 1.2: Gesimpliceerd UML model van een Luchthaven systeem<br />
1.2.1 Wat is OCL<br />
De Object Constraint Language (OCL) is een notatie taal voor analyse en design van software<br />
systemen. Deze taal laat ons toe om expressies en beperkingen op een object-georiënteerde<br />
model te schrijven. OCL is ook een deelverzameling van de Unied Modeling Language (UML).<br />
Deze constraints zijn nuttig in het software ontwikkelingsproces. Deze laten de ontwikkelaar<br />
toe om een specieke verzameling van regels die de aspecten van de individuele objecten<br />
bestuurt te creëren.<br />
OCL is ook een constructie waarbij UML het concept van Design By Contract (DBC) [25]<br />
kan ondersteunen. Er zijn twee basis soorten van constraints: klasse-invarianten en pre- en
4 HOOFDSTUK 1. INLEIDING<br />
postcondities. Het uitgangspunt is dat UML modellen met constraint declaraties concreter en<br />
preciezer zijn dan modellen die dat niet hebben. Bovendien zijn deze OCL expressies eigenlijk<br />
de invoer voor de codegeneratoren in het MDA raamwerk.<br />
Expressies geschreven in een precieze, wiskundige gebaseerde taal zoals OCL bieden een aantal<br />
voordelen ten opzichte van het puur gebruik van modellen om het software systeem te speci-<br />
ceren. Deze expressies kunnen niet op verschillende manieren geïnterpreteerd worden door<br />
analisten of door programmeurs. Deze zijn ondubbelzinnig en maken onze modellen preciezer<br />
en correcter.<br />
Ten tweede kunnen deze expressies door geautomatiseerde tools gecontroleerd worden. Codegeneratie<br />
wordt op deze manier krachtiger. Met krachtiger bedoelen we hier dat de geproduceerde<br />
artefacten formeel en precies genoeg zijn en dat er geen ingreep door de programmeurs<br />
nodig is om de gegenereerde code aan te passen of om extra broncode toe te voegen.<br />
Laat ons naar het voorbeeld over het Luchthavensysteem teruggaan. De restrictie in de multipliciteit<br />
aan de kant van Person is op te lossen door de volgende OCL constraint aan het<br />
diagram toe te voegen.<br />
context Flight<br />
inv:<br />
passengers->size()
1.4. DOELSTELLINGEN 5<br />
• Ten vierde is het mogelijk om het principe van Design By Contract in een programmeertaal<br />
te simuleren.<br />
Indien we deze aanpak in software ontwikkeling volgen, zal de productiviteit zeker verhogen.<br />
Een implementatie van een systeem door MDD/MDA tools is sneller en eciënter dan een<br />
handgeschreven implementatie. Verder is de kwaliteit van ons systeem verhoogd. Er is garantie<br />
voor een consistentie tussen ons UML/OCL- model en de gegenereerde artefacten.<br />
1.4 Doelstellingen<br />
In het kader van dit eindwerk zullen we een bepaald aspect van de modeltransformaties proberen<br />
te bestuderen - namelijk de transformaties van OCL expressies naar werkende code. Tot<br />
nu toe zijn OCL expressies vooral gebruikt als validatie van modellen en documentatie. Dus<br />
spreken we hier over statische OCL. In deze thesis willen we verder gaan door beperkingen te<br />
leggen op instantiaties van UML modellen. Dit soort van OCL expressies noemt men run-time<br />
OCL. Er zijn twee basissoorten van OCL expressies of constraints: klasse-invarianten en preen<br />
postcondities. Wat we in deze thesis willen realiseren is precies deze twee basissoorten van<br />
OCL expressies te transformeren door een specieke codegeneratie. We gebruiken JAVA als<br />
doeltaal voor deze codegeneratie.<br />
Deze specieke transformatie (codegeneratie) zal gebeuren op basis van twee aanpakken. Een<br />
eerste aanpak is de naïeve aanpak waarbij beide basissoorten van constraints gecontroleerd<br />
worden op iedere stabiel moment van een systeem. Maar hier stoppen we niet. Een tweede<br />
meer geavanceerde aanpak zal bestudeerd worden. Hierbij worden alleen de relevante OCLexpressies<br />
gecontroleerd waarbij de overhead drastisch kan dalen en de eciëntie van ons<br />
systeem verhoogt.<br />
Deze transformaties zullen we uitwerken met het HAT-tool van het software engineeringsbedrijf<br />
E2S uit Gent [10].<br />
Hiernaast zullen we ook een aantal bestaande tools evalueren die run-time OCL ondersteunen.<br />
1.5 Overzicht<br />
In dit hoofdstuk werd het ontwerp van dit eindwerk in een ruimer kader geplaatst. De doelstellingen<br />
werden kort aangehaald.<br />
In het tweede hoofdstuk gaan we de state-of-the-art van OCL toolondersteuning bespreken.<br />
Hier stellen we eerst een aantal evaluatiecriteria voor. Op basis van de opgestelde criteria<br />
evalueren we een aantal tools. Ten slotte trekken we een conclusie over de huidige toolondersteuning<br />
van run-time OCL expressies.<br />
In het derde hoofdstuk stellen we verschillende aanpakken van transformaties van constraints<br />
naar code voor. Deze constraints zijn OCL expressies op UML modellen. Eerst zullen we een<br />
beschrijving van een mogelijke OCL naar JAVA vertaling voorstellen. Daarna zullen we deze<br />
transformaties zowel op een naïeve als op een meer intelligente manier beschrijven.
6 HOOFDSTUK 1. INLEIDING<br />
In het vierde hoofdstuk beschrijven we de realisatie van OCL transformaties in het HAT tool.<br />
Ten eerste zullen we een pure vertaling van OCL expressies naar JAVA bespreken. Ten tweede<br />
zullen we de naïeve en de intelligente aanpakken bespreken.<br />
In het vijfde hoofdstuk werken we een case studie uit. De voorgestelde aanpakken in het derde<br />
hoofdstuk worden naast elkaar gelegd. Alle voordelen en nadelen zullen we grondig bespreken.<br />
Het laatste hoofdstuk is een afsluitend hoofdstuk waarin we de huidige situering en de toekomst<br />
van MDD/MDA kritisch bespreken.
Hoofdstuk 2<br />
State-of-the-art<br />
De Object Constraint Language (OCL) [1] is een notatietaal voor analyse en ontwerp van<br />
software systemen. Het is een aanvulling op de industrie standaard UML waarbij ontwikkelaars<br />
beperkingen over modellen kunnen schrijven. Vandaag de dag vereisen veel software projecten<br />
complexe formele regels die in het businessmodel moeten worden uitgedrukt. OCL lost dit<br />
probleem op. OCL is eenvoudig genoeg om door business analisten gebruikt te worden maar<br />
ook formeel genoeg om door tools geïnterpreteerd en gemanipuleerd te worden.<br />
In dit hoofdstuk geven we een overzicht van de huidige stand van tools die OCL expressies<br />
naar JAVA vertalen. Hier gaan we eerst een aantal evaluatiecriteria voorstellen. Dan zullen<br />
we een aantal tools evalueren aan de hand van de voorgestelde criteria. Vervolgens zullen we<br />
deze tools naast mekaar zetten.<br />
2.1 Evaluatiecriteria<br />
In deze sectie gaan we een structuur voorstellen om tools te kunnen evalueren. Deze structuur<br />
is niet alleen gebaseerd op constructies die in de OCL taal aanwezig zijn maar ook op andere<br />
factoren: bijvoorbeeld de code die gegenereerd wordt na de vertaling van de OCL expressies<br />
naar een doel taal. In deze thesis wordt JAVA gebruikt als doeltaal voor de vertaling.<br />
In deze thesis onderzoeken we alleen maar een deel van de eigenschappen van OCL. We<br />
gaan voornamelijk in op klasse-invarianten, pre- en postcondities. Ook onderzoeken we de<br />
ondersteuning van meer ingewikkelde sleutelwoorden zoals @pre en result. Ten laatste breiden<br />
we onze criteria uit met manieren om de voldoening van constraints na te gaan. Daarom zijn<br />
onze criteria ook beperkt tot deze deelverzameling.<br />
In de context van OCL kunnen we meer informatie aan een model toevoegen aan de hand van<br />
invarianten. Deze invarianten kunnen op verschillende model elementen inwerken.<br />
2.1.1 Invarianten<br />
In specicaties is zo dat we vaak met invarianten te maken hebben. Klasse-invarianten garanderen<br />
ons dat objecten altijd een geldige toestand hebben. We zullen klasse-invarianten<br />
in meer detail in subsectie 3.1.1 bespreken. Daarom is de ondersteuning van invarianten door<br />
tools een basisvereiste.<br />
7
8 HOOFDSTUK 2. STATE-OF-THE-ART<br />
2.1.2 Pre- en Postcondities<br />
Indien men een meer complete specicatie van een systeem wenst is belangrijk om gebruik te<br />
maken van pre- en postcondities. Pre- en postcondities zijn beperkingen die de eecten en<br />
toepasbaarheid van operaties speciceren. In subsectie 3.1.2 zullen we dieper ingaan op preen<br />
postcondities.<br />
Sommige tools [13] beperken zich alleen maar tot precondities. De argumenten achter deze<br />
beslissing zijn dat de gebruiker de regels van een contract moet volgen. Indien dat niet zo is,<br />
is er geen garantie dat het resultaat van het uitvoeren van een stuk code correct zal verlopen.<br />
Verder wordt ook verondersteld dat het uitvoeren van een stuk code altijd correct beëindigt<br />
indien de precondities niet geschonden zijn.<br />
In deze thesis vertegenwoordigen we het gebruik van pre- en postcondities. De vereisten zijn<br />
dat een tool beide soorten van constraints ondersteunt.<br />
We hebben al twee soorten van constraints gezien: klasse-invarianten, pre- en postcondities.<br />
Bij elk van deze soorten van constraints zijn er verschillende modelelementen betrokken, namelijk<br />
attributen, associatie, operaties, enz.<br />
2.1.3 Attributen en Associaties<br />
Indien de visibiliteit van attributen en associaties als private gemodelleerd is dan zijn deze<br />
beperkt tot de klasse waarin ze gedenieerde zijn. Om naar deze modelelementen te kunnen<br />
refereren is het essentieel om inspectoren voor deze modelelementen te denieren.<br />
Indien een tool ondersteuning biedt voor invarianten en pre- en postcondities moet het tool<br />
minimaal een vertaling van eenvoudige constraints ondersteunen. Met eenvoudige constraints<br />
bedoelen we constraints die bijvoorbeeld alleen maar attributen en operaties op deze attributen<br />
bevatten. Ook vallen enkelvoudige associaties in deze categorie. Indien de associaties een<br />
collectie voorstellen is dat niet meer het geval. Dit wordt uitgelegd in de volgende paragraaf.<br />
2.1.4 Operaties op collecties<br />
Er bestaan twee soorten van operaties op collecties: zij die over de collecties itereren en zij<br />
die dat niet doen. Hier focussen we op de eerste soort, namelijk collectieiteratoren. In JAVA<br />
hebben OCL collectieiteratoren geen tegenhangers. Bijgevolg verkrijgen we meestal complexe<br />
constructies na de vertaling van deze collectieiteratoren naar een doeltaal. Wat we hier willen<br />
onderzoeken is in welke mate de complexiteit van deze constructies kan worden gereduceerd.<br />
2.1.5 @pre en result<br />
In OCL zijn er ook meer geavanceerde constructies voor postcondities. Hier bedoelen we<br />
constructies die naar waarden van modelelementen op twee verschillende tijdstippen refereren,<br />
bijvoorbeeld de OCL constructie @pre. Een andere constructie is een constructie die het result<br />
sleutelwoord bevat. Dit sleutelwoord duidt de terugwaarde van een operatie aan. Het type<br />
van result hangt af van het terugtype van de operatie.<br />
Beide sleutelwoorden kunnen de specicatie van een model verbeteren en hun vertaling naar<br />
een doeltaal is hier gewaardeerd
2.1. EVALUATIECRITERIA 9<br />
2.1.6 Assertion<br />
Hoewel deze criteria cruciaal zijn ontbreekt nog een deel van de puzzel. Is de transformatie<br />
uiteindelijk betrouwbaar? Wat gebeurt als een constraint geschonden is? Is er een exceptie<br />
gegooid? Dat zijn allemaal vragen die men kan stellen.<br />
Hiervoor gebruiken we de techniek van assertion [14]. In sommige programmeertalen is dat<br />
volledig ondersteunt of helemaal niet. Try-catch blokken zijn geen slechte manier van werken.<br />
Maar bij het uitvoeren van een systeem zal elke constraint nagegaan worden. Bij het installeren<br />
(deployment) leidt dat tot een vertraagd systeem. Daarom verkiezen we voor de assertion<br />
techniek die ons toelaat om de beslissing te nemen over het aan- en uitschakelen van de<br />
constraints op run-time. Een code die geen van deze twee technieken gebruikt beschouwen we<br />
als slechte code.<br />
2.1.7 Slimme controle van klasse-invarianten<br />
De criteria die we hanteren wat betreft de technieken voor het nagaan van voldoening van<br />
constraints hebben we hierboven beschreven. Eveneens van belang is de vraag of we telkens<br />
alle klasse-invarianten moeten nagaan na het uitvoeren van een publieke methode. Misschien<br />
is het wel beter om enkel de relevante klasse-invariaten te inspecteren. In hoofdstuk 3 gaan we<br />
een mogelijke intelligente methode voorstellen om na het uitvoeren van een publieke methode<br />
alleen maar relevante klasse-invarianten na te gaan.<br />
Alles wat we tot nu toe besproken hebben is samengevat in tabel 2.1. Deze tabel somt een<br />
aantal criteria op en geeft voor elke vereiste een beschrijving.<br />
Id Criteria Beschrijving<br />
1 invarianten Garanderen een geldige toestand van objecten<br />
2 pre-, postcondities Vervolledigen de specicatie door constraints<br />
op operaties toe te voegen<br />
3 attributen, Basismodelelementen in een OCL<br />
associaties<br />
expressie<br />
4 collectieiteratoren OCL collectieiteratoren hebben geen substituten in<br />
JAVA - hoe worden hulp operaties gegenereerd<br />
5 @pre Verbeteren de specicatie van postcondities<br />
door naar waarden op verschillende tijdstippen<br />
te refereren<br />
6 result Verbeteren de specicatie van postcondities(geeft<br />
de terugwaarde van operaties terug)<br />
7 assertion De manier van controle: zijn de constraints<br />
al dan niet geschonden<br />
8 Slimme controle van Zijn alle constraints nagegaan of maar deze<br />
klasse-invarianten die relevant zijn<br />
Tabel 2.1: Evaluatiecriteria voor transformatie van OCL expressies door een tool
10 HOOFDSTUK 2. STATE-OF-THE-ART<br />
2.2 Tools<br />
Er zijn al een aantal tools op de markt die ondersteuning en afhandeling van OCL expressies<br />
bieden. Hieronder geven we een overzicht van de tools waarmee we geëxperimenteerd hebben.<br />
We voegen ook extracten (skeletten) van code toe die door de tools gegenereerd waren. Sommige<br />
methodes kunnen leeg zijn maar dat is hier niet van belang want we willen de structuur<br />
van de gegenereerde code tonen.<br />
2.2.1 Octopus<br />
Octopus [13] is een tool dat het gebruik van OCL ondersteunt. Octopus staat voor OCL tool<br />
for Precise UML Specication. Het Octopus tool biedt twee belangrijke functionaliteiten. Ten<br />
eerste is in Octopus mogelijk om zowel de syntaxis van OCL expressies als de typen en het<br />
juiste gebruik van modelelementen na te gaan. Met modelelementen wordt hier attributen en<br />
associaties bedoeld. Ten tweede biedt Octopus de mogelijkheid om transformaties van UML<br />
modellen met de bijhorende OCL expressies (beperkingen die in OCL uitgedrukt zijn) naar<br />
JAVA uit te voeren.<br />
Dit tool biedt geen mogelijkheden om modellen te creëren. De modellen moeten eerst in andere<br />
modelleertools zoals bijvoorbeeld MagicDraw [22] gedenieerd worden en pas dan geïmporteerd<br />
worden. Dan kan men de specicatie uitgedrukt in OCL toevoegen. Vervolgens volstaat<br />
het om de codegenerator op te roepen. Het resultaat van de transformatie is een model in<br />
een platformafhankelijke taal. In dit tool is er geen mogelijkheid om de gegenereerde code te<br />
ne-tunen, namelijk om de structuur van de geproduceerde code (de elementen die het tool<br />
genereert) te veranderen.<br />
Id Criteria Opmerking Ondersteuning<br />
1 invarianten Ja<br />
2 pre-, postcondities allen precondities Ja<br />
3 attributen, inspectoren en mutatoren Ja<br />
associaties zijn gegenereerd<br />
4 collecties Voor ieder collectieiterator Ja<br />
een nieuwe methode<br />
5 @pre Neen<br />
6 result Neen<br />
7 assertion Alleen precondities zijn Ja<br />
door assert nagegaan<br />
8 slimme controle Neen<br />
klasse-invarianten<br />
Tabel 2.2: Evaluatie van Octopus<br />
De code die hier gegenereerd wordt, is volledig in de zin dat niet alleen de OCL expressie zijn<br />
vertaald naar JAVA maar er zijn ook constructoren, inspectoren en mutatoren gegenereerd.<br />
Met andere woorden verkrijgen we een volledig uitvoerbaar systeem. Het tool ondersteunt
2.2. TOOLS 11<br />
alleen maar invarianten en precondities. In de laatste versie 2.2.0 die we getest hebben, zijn<br />
postcondities nog niet ondersteund. Bijgevolg zijn sleutelwoorden als @pre en result ook niet<br />
vertaald.<br />
Iedere keer een OCL expressie gebruik maakt van collectieoperaties die over collecties itereren<br />
zijn er hulp methoden voor gecreëerd. Op deze manier wordt de code met dupliceerde<br />
hulp methoden aangevuld. Bij operaties zijn er wel de precondities gecheckt maar niet de<br />
invarianten.<br />
Hieronder geven we een skelet van hoe de gegenereerde JAVA code eruit ziet:<br />
Listing 1 Octopus<br />
class X{<br />
private int attribute;<br />
public X(){<br />
}<br />
public void operation(){<br />
assert (precondition);<br />
//... <br />
}<br />
private boolean helpMethod(){<br />
}<br />
public void inv0(){<br />
boolean result = helpMethod();<br />
if(!result) throw new InvariantException();<br />
public void inv1(){<br />
}<br />
public List checkAllInvariants(){<br />
List result = new ArrayList();<br />
try{<br />
inv0();<br />
}catch(InvariantException e){result.add(e);}<br />
try{<br />
inv1();<br />
}catch(InvariantException e){result.add(e);}<br />
return result;<br />
}<br />
}<br />
2.2.2 OCLE<br />
OCLE [12] is een UML CASE tool dat OCL volledig ondersteunt, zowel op metamodel als<br />
op modelniveau. De codegenerator produceert code voor de modelstructuur en voor de OCL<br />
specicatie. Het resultaat is een volledig werkende code. Versie 2.0 van het tool biedt ondersteuning<br />
voor OCL 2.0 en is compatibel met UML 1.5.<br />
Indien men dit tool voor codegeneratie wil gebruiken moet men eerst een model in het tool<br />
creëren en dan de bijbehorende OCL constraints toevoegen. Deze constraints kunnen geva-
12 HOOFDSTUK 2. STATE-OF-THE-ART<br />
lideerd worden voordat men code gaat genereren. Men kan niets aan de structuur van de<br />
gegenereerde code veranderen. Bovendien om de code te kunnen compileren moet men een<br />
bibliotheek importeren die bij het tool te vinden is.<br />
Alhoewel pre- en postcondities ondersteund zijn, worden de sleutelwoorden @pre en result niet<br />
correct geimplementeerd. Dat is te wijten aan het feit dat deze sleutelwoorden genegeerd zijn<br />
door de codegenerator.<br />
De controle over de constraints gebeurt door if-blokken. Indien de beperkingen in de specicatie<br />
geschonden zijn, wordt een boodschap getoond. Er is geen mogelijkheid om deze controles<br />
aan en uit te schakelen wanneer dat gewenst is. Dat is een min punt voor dit tool vermits<br />
volgens B.Meyer pre- en postcondities tijdens testen of debuggen moeten aangezet worden<br />
maar tijdens de echte uitvoering van het programma moeten afstaan [25].<br />
Bij collectieiteratoren worden geen hulpmethodes gegeneerd. Alle bewerkingen rond collectieiteratoren<br />
zijn te vinden in de methode van de invariant of pre- en postconditie zelf. Bijgevolg<br />
zijn de gegenereerde JAVA methoden langer en soms niet overzichtelijk.<br />
Hieronder tonen we in Listing 2 hoe een klasse met de bijbehorende specicatie eruit ziet.<br />
Listing 2 OCLE<br />
class X{<br />
private int attribute;<br />
public void operation(){<br />
class ConstraintChecker{<br />
public void checkPreconditions(){<br />
}<br />
public void checkPostconditions(){<br />
}<br />
ConstraintChecker ch = new ConstraintChecker();<br />
ch.checkPreconditions();<br />
ch.checkPostconditions();<br />
}<br />
}<br />
public class ConstaintChecker{<br />
public void checkConstaints(){<br />
check_X_inv0();<br />
check_X_inv1();<br />
}<br />
public void inv0(){<br />
//... <br />
if(!test)<br />
System.out.println(postcondition failed for object+X.this);<br />
}<br />
public void inv1(){<br />
}<br />
}<br />
}<br />
De gegenereerde code verkrijgen uit het OCLE tool is min of meer volledig in de zin dat we een
2.2. TOOLS 13<br />
compleet systeem als resultaat hebben. Natuurlijk zijn meer geavanceerde constructie (zoals<br />
@pre en result) nog niet ondersteund. In de tabel 2.3 is alles nog eens samengevat.<br />
Id Criteria Opmerking Ondersteuning<br />
1 invarianten Ja<br />
2 pre-, postconditie Ja<br />
3 attributen, Geen inspectoren of Neen<br />
associaties mutatoren gegenereerd,<br />
attributen met publieke visibiliteit<br />
4 collectie Ja<br />
5 @pre dit sleutelwoord is Neen<br />
genegeerd<br />
6 result Neen<br />
7 assertion Neen<br />
8 slimme controle Neen<br />
klasse-invarianten<br />
Tabel 2.3: Evaluatie van OCLE<br />
2.2.3 Together 2006 for Eclipse<br />
De Together 2006 Release 2 for Eclipse [11] versie 8.1.1 is een visueel platform dat ondersteuning<br />
biedt voor een grote groep van gebruikers, van software architecten en ontwikkelaars tot<br />
business process analisten. De mogelijkheden van dit tool zijn enorm groot. Hier gaan we ons<br />
tot een bepaald deel van de eigenschappen van het tool beperken, namelijk transformatie van<br />
OCL expressies naar code.<br />
Id Criteria Opmerking Ondersteuning<br />
1 invarianten Ja<br />
2 pre-, postconditie Ja<br />
3 attributen, Geen mutatoren of inspectoren, Neen<br />
associaties publieke visibiliteit attributen<br />
4 collectie Geen hulp methoden Ja<br />
5 @pre Ja<br />
6 result De semantiek van de vertaalde Ja<br />
code is niet precies<br />
7 assertion Goed gebruik van assertions Ja<br />
slimme controle<br />
8 klasse-inavianten Neen<br />
Tabel 2.4: Evaluatie van Together 2006 for Eclipse<br />
In dit tool kan men opnieuw zelf modellen creëren en dan constraints erop toevoegen. Het tool<br />
beschikt over een 'syntax completion' functie die men toelaat om snel en eciënt constraints
14 HOOFDSTUK 2. STATE-OF-THE-ART<br />
te schrijven. Controle voor geldigheid en consistentie van constraints is door het tool gedaan.<br />
Er is geen mogelijkheid om de structuur van de gegenereerde code te veranderen.<br />
Een van de positieve punten van dit tool is dat het tool ondersteuning biedt voor zowel<br />
invarianten als voor pre- en postcondities. Binnen elke gegenereerde klasse uit het model is<br />
een statische klasse gedenieerd met de bijbehorende constraints. Het skelet van een klasse<br />
ziet als volgt uit:<br />
Listing 3 Together<br />
class X{<br />
private int attribute;<br />
public void operation(){<br />
assert ( OCL.preOperation());<br />
// ... <br />
assert(OCL.AllInvariants() && OCL.postOperation());<br />
}<br />
protected static class OCL{<br />
private OCL(){<br />
}<br />
static boolean inv0(){<br />
}<br />
static boolean inv1(){<br />
}<br />
static boolean allInvariants(){<br />
return inv0()&& inv1();<br />
}<br />
static boolean preOperation(){<br />
}<br />
static boolean postOperation(){<br />
}<br />
}<br />
}<br />
Het tool genereert code voor alle attributen, associaties en operaties en natuurlijk ook voor<br />
de constraints uitgedrukt in OCL. Constructoren, mutatoren en inspectoren worden niet gegenereerd.<br />
De vertaalde OCL code refereert naar de attributen of associaties zelf en niet naar<br />
hun inspectoren. Vermits visibiliteit van attributen meestal beperkt is tot de klasse waarin ze<br />
gedenieerd zijn, is de gegenereerde code soms niet compileerbaar omwille van het probleem<br />
van visibiliteit. Een oplossing is om alle attributen publiek te maken wat niet altijd gewenst is.<br />
Operaties op collecties zijn ook ondersteund. Indien in een OCL expressie collectieiteratoren<br />
aanwezig zijn, zijn deze niet ingekapseld in aparte methoden maar tussen try-catch blokken<br />
binnen de invariant of pre- en postconditie methode zelf. Dat leidt bij complexe OCL expressie<br />
tot lange en soms niet overzichtelijke methoden met een aantal grote try-catch blokken.<br />
Indien we de code structuur van de gegenereerde code bekijken, zien we dat de controle op de<br />
constraints gebeurt door het gebruik van asserties. Dat beschouwen we als een goede techniek.<br />
Postcondities mogen meer geavanceerde constructies hebben. De sleutel worden @pre en result<br />
zijn geimplementeerd.
2.2. TOOLS 15<br />
Indien het sleutelwoord @pre in een van de postcondities aanwezig is dan is de structuur van<br />
de klasse waarbij deze postconditie behoort uitgebreid. Deze uitbreiding is gevat in een nieuwe<br />
abstracte statische klasse PrePost. Deze nieuwe klasse is een hulpklasse om de toestand van<br />
een variabele of object op te slaan voor de uitvoering van de operatie. Op deze manier worden<br />
pre- en postcondities correct geëvalueerd.<br />
We kunnen nu alles recapituleren in tabel 2.4.<br />
Listing 4 Together 2<br />
class X{<br />
private int attribute;<br />
public void operation(){<br />
OCL.PrePost oclPreState = OCL.preOperation();<br />
// ... <br />
assert(OCL.PrePost.checkPost(oclPreState,null) && OCL.AllInvariants() );<br />
}<br />
protected static class OCL{<br />
private static final boolean isEnabled = X.class.desiredAssertStatus();<br />
private OCL(){<br />
}<br />
protected static abstract class PrePost{<br />
private static boolean checkPost(PrePost preState, Object result){<br />
return preState != null ? preState.post(result) : !isEnabled;<br />
}<br />
protected abstract boolean post(Object result);<br />
}<br />
static protected PrePost preOperation(){<br />
if(!OCL.isEnabled) return null;<br />
PrePost preState = new PrePost(){<br />
// save the preState of the postcondition<br />
protected boolean post(Object _result){<br />
// ... <br />
}<br />
};<br />
return preState;<br />
}<br />
static boolean inv0(){<br />
}<br />
static boolean inv1(){<br />
}<br />
static boolean allInvariants(){<br />
return inv0()&& inv1();<br />
}<br />
static boolean preOperation(){<br />
}<br />
}<br />
}
16 HOOFDSTUK 2. STATE-OF-THE-ART<br />
2.2.4 Conclusie<br />
Er bestaan veel tools die statische OCL ondersteunen, namelijk voor validatie en documentatie<br />
van modellen. Bovendien zijn ze tegenwoordig betrouwbaar om mee te kunnen werken.<br />
Indien we run-time OCL willen gebruiken, is dat niet meer het geval. Het aantal tools dat<br />
deze soort van OCL ondersteuning aanbieden, is beperkt. Daarenboven is de code die ze<br />
genereren meestal niet volledig, of nog erger niet correct. In dit hoofdstuk hebben we de<br />
huidige ondersteuning van run-time OCL generatie onderzocht. De conclusie is dat de huidige<br />
state-of-the-art tools onvolledig zijn wat betreft de criteria opgesteld in het begin.<br />
In het volgende hoofdstuk gaan we zelf een transformatie uitwerken gebruik makende van het<br />
HAT tool en zien in welke mate die de criteria kan ondersteunen. Maar eerst zullen we een<br />
methodologie uitwerken voor het omzetten van OCL expressies naar JAVA code.
Hoofdstuk 3<br />
Methodologie<br />
In dit hoofdstuk bespreken we eerst wat Design By Contract (DBC) is. Hier worden klasseinvarianten,<br />
pre- en postcondities grondig uitgelegd. Verder leggen we uit hoe DBC in programmeertalen<br />
zoals JAVA kan gesimuleerd worden die per denitie geen expliciete ondersteuning<br />
van DBC heeft. Na deze inleiding over DBC gaan we uitleggen welke aanpakken we<br />
in deze thesis hebben gebruikt om de doelstellingen te kunnen bereiken. De eerste aanpak<br />
is redelijk naïef. Deze aanpak is eenvoudig maar veroorzaakt in sommige gevallen veel overhead.<br />
Een tweede aanpak die we ontwikkeld en bestudeerd hebben, noemen we de intelligente<br />
aanpak. Deze is meer eciënt dan de naïeve aanpak ten opzichte van de uitvoeringstijd maar<br />
tegelijkertijd is de complexiteit van de structuur die uiteindelijk de broncode gaat genereren<br />
ook verhoogd.<br />
3.1 Design By Contract<br />
Desing By Contract [25] is een methode voor het ontwikkelen van kwaliteitssoftware bedacht<br />
door Bertrand Meyer. DBC focusseert zich op het maken van software contracten op een<br />
expliciete en formele manier. Deze software contracten speciceren wat elke operatie in een<br />
systeem van de oproeper vereist en wat aan de oproeper gegarandeerd wordt.<br />
De denitie van een contract is in het DBC principe afgeleid van de notie van een contract.<br />
Een contract is een wettelijke overeenkomst tussen twee partijen waarin beide partijen alle<br />
verbintenissen aanvaarden. In termen van object-georiënteerde ontwikkeling is een contract<br />
een middel om de verantwoordelijkheden van objecten op een precieze en niet ambigu manier<br />
vast te leggen. Een object is aansprakelijk voor het uitvoeren van diensten (verplichten) enkel<br />
en alleen als sommige voorwaarden voldaan zijn. Een contract is een nauwkeurige specicatie<br />
van de interface van een object. Alle objecten die gebruik willen maken van de aangeboden<br />
diensten heten klanten of consumenten. Het object dat deze diensten aanbiedt noemt men de<br />
leverancier of producent.<br />
Alhoewel de notie van contract afkomstig van de rechten praxis is, wijkt het contract beetje af<br />
wanneer het men in de context van object-georiënteerde ontwikkeling neerzet. Namelijk een<br />
contract is aangeboden door een leverancier al dan niet een klant aanwezig is. Echter als een<br />
klant gebruik van de aangeboden diensten in een contract maakt, verplicht de klant zich aan<br />
de voorwaarden in het contract.<br />
17
18 HOOFDSTUK 3. METHODOLOGIE<br />
Een contract beschrijft de diensten die door een object aangeboden worden. Voor elke dienst<br />
speciceert het contract:<br />
• Onder welke voorwaarden de dienst zal aangeboden worden.<br />
• De specicatie van het resultaat van de aangeboden dienst gegeven dat alle voorwaarden<br />
voldaan zijn.<br />
Elk object kan een aantal operaties uitvoeren. Deze operaties zijn te vinden in de interface<br />
van het object zelf. Voor elke van deze operaties kan een contract geëxpliciteerd worden.<br />
De rechten van het object komen met precondities overeen en de verplichten van het object<br />
corresponderen met de postcondities.<br />
Indien minstens een van beide partijen de voorwaarden van het contract niet respecteren, is<br />
het contract geschonden. Als dat gebeurt dan is het duidelijk wie aan de voorwaarden van<br />
het contract niet voldeed. Ofwel heeft de klant de aangeboden diensten niet correct gebruikt,<br />
ofwel heeft de leverancier de aangeboden dienst niet correct uitgevoerd. De programmeertaal<br />
EIFFEL [19] is de enige taal die een implementatie van DBC voorziet. DBC kan gebruikt<br />
worden binnen de context van object-georiënteerde ontwikkeling.<br />
In volgende sectie zullen we bespreken wat klasse-invarianten, pre- en postcondities zijn.<br />
3.1.1 Klasse-invarianten<br />
Klasse-invariant - is een booleaanse expressie die een conditie vaststelt. Deze conditie moet<br />
altijd voldaan zijn voor alle instanties waarvoor deze conditie gedenieerd is. Een invariant<br />
moet juist na de afhandeling van een constructor of na de afhandeling van elke publieke<br />
operatie waar zijn maar niet noodzakelijk tijdens het uitvoeren van de operatie. Anders<br />
gezegd, een klasse-invariant moet waar zijn op eender welke consistente toestand van een<br />
systeem. Indien het systeem bijvoorbeeld een operatie uitvoert dan is ons systeem niet in een<br />
consistente toestand en bijgevolg moet de invariant niet meer waar zijn. Uiteraard moet na<br />
de uitvoering van de operatie de invariant opnieuw waar zijn.<br />
3.1.2 Precondities en postcondities<br />
Pre- en postcondities zijn gebruikt om operaties te speciceren. Deze vervolledigen de interface<br />
van een operatie. Pre- en postcondities speciceren niet hoe het lichaam van een operatie<br />
geimplementeerd moet worden. Hieronder zijn deze concepten gedenieerd:<br />
Preconditie - is een booleaanse expressie die op het moment dat het uitvoeren van de operatie<br />
begint waar moet zijn.<br />
Postconditie - is een booleaanse expressie dat op het moment dat het uitvoeren van de operatie<br />
eindigt waar moet zijn.<br />
Met andere woorden pre- en postcondities moeten alleen op bepaalde tijdstippen (respectievelijk<br />
voor en na het uitvoeren van operaties) waar zijn in tegenstelling tot invarianten die<br />
altijd waar moeten zijn.
3.1. DESIGN BY CONTRACT 19<br />
Het principe achter pre- en postcondities is meestal gerefereerd naar het principe van Design<br />
By Contract (DBC) dat wij in de vorige sectie uitgelegd hebben.<br />
Vermits niet alle programmeertalen het principe van DBC ondersteunen, kan DBC op een of<br />
ander manier gesimuleerd worden in andere programmeertalen. Volgende sectie introduceert<br />
het begrip assertion en meer bepaald hoe dat kan geimplementeerd worden in de programmeertaal<br />
JAVA.<br />
3.1.3 Assertions in Java<br />
Een assertie (assertion) [14] is een booleaanse expressie die ons toelaat om een veronderstelling<br />
over een programma te toetsen. Als deze veronderstelling niet waar is, zal het programma een<br />
exceptie gooien. Indien de vericatie van deze booleaanse expressie waar levert dan bevestigt<br />
de corresponderende assertion de gemaakte veronderstelling. Als gevolg krijgt men foutsvrije<br />
programmas en de kwaliteit van de programmas is verhoogd.<br />
Ervaring toont dat het gebruik van assertions tijdens programmeren een van het snelste en<br />
meest eectieve manieren is om fouten te detecteren en corrigeren. Bovendien dienen assertions<br />
voor documentatie van programmas en op deze manier is de onderhoud verbeterd.<br />
In JAVA heeft assertion de volgende vorm:<br />
assert Expressie1 : Expressie2 ;<br />
waarbij Expressie1 een booleaanse expressie is en Expressie2 een expressie is die een waarde<br />
heeft (deze expressie kan geen invocatie van een methode zijn die als void gedenieerd is). Het<br />
systeem evalueert Expressie1 en als dat false is dan gooit het systeem een AssertionError.<br />
Dan wordt de waarde van Expressie2 doorgegeven aan de constructor van AssertionError die<br />
de stringrepresentatie van de waarde van Expressie2 gebruikt om een gedetailleerde bericht<br />
van de fout te geven.<br />
De assert constructie is een informele soort van Design By Contract. Toch kan men een<br />
ondersteuning voor klasse-invarianten, pre- en postcondities bieden. De assert constructie<br />
dwingt ons niet om een bepaalde soort van constraint-checking te gebruiken. Meestal is gelegen<br />
om de expressies die constraints op de objecten van ons systeem opleggen te combineren in een<br />
enkele booleaanse methode. Dan wordt deze booleaanse methode opgeroepen door de assertie<br />
clausule.<br />
3.1.3.1 Waarom assertie gebruiken?<br />
Veel mensen zouden zich afvragen waarom men een assertie faciliteit moet aanbieden. Deze<br />
speciale ondersteuning van assertie in de JAVA programmeertaal kan expliciet geprogrammeerd<br />
worden.<br />
Alhoewel een ad hoc implementatie mogelijk is is het meestal minder gestructureerd (ifstatements<br />
nodig voor elke assert) en ineciënt (evaluatie van de condities zelfs als asserties<br />
uitgeschakeld zijn). Ten tweede heeft elke ad hoc implementatie zijn eigen manier van aanen<br />
uitschakelen van asserties. Dit reduceert het nut van deze implementaties vooral bij debuggen.<br />
Bijgevolg is de assertie cultuur nooit gevolgd door software ingenieurs in de JAVA<br />
programmeertaal.
20 HOOFDSTUK 3. METHODOLOGIE<br />
Het grootste voordeel van het assert statement in de JAVA programmeertaal is de mogelijkheid<br />
om deze asserties aan en uit te schakelen. Bij de implementatie- en testfasen van het<br />
ontwikkelingsproces is essentieel om deze opties aan te schakelen. Als we in plaats van asserties<br />
gewoon if-statements gebruiken om de constraints over ons model te veriëren maakt<br />
dat geen groot verschil. Het nadeel van de if-statements tegenover de asserties komt bij de<br />
fase van het installeren (deployment) van applicaties. Deze overhead van zulke vericatie kan<br />
enorm groot zijn waardoor de eciëntie van ons systeem kan dalen. If-statements zullen in<br />
alle gevallen uitgevoerd worden. Gebruik makende van asserties kunnen wij op elk moment<br />
beslissen of we al dan niet de constraints willen nagaan.<br />
3.2 Beschrijving van de methodologie<br />
In deze sectie zullen we een formele beschrijving van OCL expressies naar broncode geven, in<br />
ons geval is dat JAVA. We zullen ons op een deelverzameling van de OCL taal focussen. Deze<br />
deelverzameling zal attribuutmodelelementen, associatiemodelelementen, operaties en meer<br />
geavanceerde constructies zoals tijdgerefereerde variabelen bevatten.<br />
3.2.1 Vertaling van attributen en associaties<br />
De aanwezigheid van attributen en associatie is niet zo maar toevallig in OCL expressies.<br />
Attribuutwaarden stellen eigenschappen van klassen voor. Anderzijds representeren associatiewaarden<br />
de relaties tussen de verschillende klassen in een model. Bij gevolg hebben allebei<br />
een belangrijke rol en hun vertaling is wel nodig.<br />
Een attribuut of associatie kan in een OCL expressie zowel expliciet als impliciet gerefereerd<br />
worden. Indien men expliciet naar een contextuele instantiatie moet refereren, kan men gebruik<br />
maken van het sleutelwoord self. Als de referentie naar een contextuele instantiatie<br />
(in ons geval een attribuut of associatie) duidelijk is, is dit sleutelwoord optioneel.<br />
Bij vertaling van het OCL sleutelwoord self naar JAVA beeld dit zich af op het sleutelwoord<br />
this in JAVA. De waarde van het attribuut attribute is in JAVA te verkrijgen door de operatie<br />
getAttribute() op te roepen. Eender welke attribuutreferentie in OCL wordt op bijbehorende<br />
get operatie afgebeeld. Als we de volgende OCL expressie hebben:<br />
self.attribute<br />
wordt deze vertaald in JAVA op de volgende manier.<br />
this.getAttribute()<br />
De JAVA vertaling van associaties is als volgt: associaties worden afgebeeld op private JA-<br />
VA attributen met de bijbehorende get operatie. Dat wil zeggen dat OCL navigatie binnen<br />
associaties als een operatie oproep in JAVA gemodelleerd wordt. De OCL expressie
3.2. BESCHRIJVING VAN DE METHODOLOGIE 21<br />
self.associationEnd<br />
wordt getransformeerd naar de volgende JAVA code:<br />
this.getAssociationEnd();<br />
3.2.2 Vertaling van OCL operaties over basistypen<br />
Deze sectie denieert de vertaling van operaties over basistypen. Met basistypen bedoelen<br />
we hier de Boolean, Integer, Real en String typen. De vertaling van deze basistypen en<br />
hun bijbehorende operaties is op zichzelf niet zo moeilijk. We kunnen voor elke van deze<br />
operaties een implementatie in JAVA voorzien door middel van een van standaard bibliotheek<br />
waarin voor elk type een klasse zal geimplementeerd worden en binnen zo'n klasse zullen de<br />
bijbehorende operaties als statische methoden geimplementeerd worden. Vermits in JAVA<br />
deze typen en hun bijbehorende operaties tegenhangers hebben verkiezen we hier om een<br />
hergebruik van de JAVA API te maken. Bijgevolg is de vertaling van deze OCL operaties<br />
eigenlijk een afbeelding op hun tegenhangers in JAVA. We zullen kort de vertaling van de meest<br />
voorkomende operaties bespreken. We zullen deze vertaling ook in een tabelvorm voorstellen.<br />
We hebben al gezegd dat er Boolean, Integer, Real en String basistypen in OCL zijn. We<br />
beginnen met het Boolean basistype.<br />
3.2.2.1 Boolean type<br />
Een boolean type kan twee waarden hebben: false of true. JAVA biedt equivalente sleutelwoorden<br />
en bijgevolg is de vertaling naar JAVA direct. Natuurlijk moeten de bijbehorende operaties<br />
wel afgebeeld worden. In tabel 3.1 introduceren we het meest voorkomende booleaanse operaties.<br />
Booleaanse operaties hebben twee delen, namelijk een rechter- en een linkerdeel. In<br />
de tabel hieronder stelt 'a' het linker deel van deze operaties voor die een OCL expressies<br />
is. Hetzelfde geldt voor het rechterdeel. Het resultaat van het toepassen van een booleaanse<br />
operaties geeft een booleaanse waarde terug.<br />
Operatie OCL notatie Java vertaling Type van het resultaat<br />
or a or b a || b Boolean<br />
and a and b a && b Boolean<br />
exclusieve or a xor b (a || b) &&(a != b) Boolean<br />
negation not a ! a Boolean<br />
equals a = b a == b Boolean<br />
not equals a b a != b Boolean<br />
implies a implies b a ? b : true Boolean<br />
Tabel 3.1: Standaard operaties voor de Boolean type<br />
We gaan dat verduidelijken aan de hand van een voorbeeld voor de conjunctie en disjunctie<br />
van twee booleaanse attributen. Stel dat we de volgende OCL expressie hebben:
22 HOOFDSTUK 3. METHODOLOGIE<br />
self.attr1 and self.attr2 or self.attr3<br />
Dit wordt getransformeerd naar de volgende JAVA code:<br />
this.getAttr1() && this.getAttr2() || this.getAttr3();<br />
De volgorde van deze booleaanse operaties is voor de JAVA vertaling ook behouden bij de<br />
OCL expressie van daarboven.<br />
3.2.2.2 Real en Integer types<br />
Een Integer type stelt de natuurlijke getallen voor. Een Real type representeert het wiskundige<br />
concept van de reële getallen. Bijgevolg zijn integers een subtype van de reële getallen.<br />
Integers hebben tegenhangers in JAVA. Voor het Real type biedt JAVA float en double type<br />
aan. Men moet bij de vertaling een van deze twee types kiezen. De standaard operaties over<br />
Integer en Real types mogen op de java.lang.Math API afgebeeld woorden. In tabel 3.2<br />
hieronder geven we een JAVA vertaling van deze operaties.<br />
Operatie Notaties Java vertaling Resultaat<br />
equals a = b a == b Boolean<br />
not equals a b a != b Boolean<br />
less a < b a < b Boolean<br />
more a > b a > b Boolean<br />
less or equal a
3.2. BESCHRIJVING VAN DE METHODOLOGIE 23<br />
3.2.2.3 String type<br />
Voor de vertaling van operaties over strings kan een groot deel van de java.lang.String API<br />
opnieuw gebruikt worden. De operatie size() moet naar de methode length() in JAVA<br />
vertaald worden. Indien we de gelijkheid van twee strings willen nagaan kunnen we in JAVA<br />
gebruik maken van de equals() methode. Dit is samengevat in tabel 3.3.<br />
Operaties Notaties Java vertaling Resultaat<br />
concatenation str.concat(str) str.concat(str) String<br />
size str.size() str.length() Integer<br />
to lower case str.toLower() str.toLowerCase() String<br />
to upper case str.toUpper() str.toUpperCase() String<br />
substring str.substring(int,int) str.substring(int,int) String<br />
equals str1 = str2 str1.equals(str2) Boolean<br />
not equals str1 str2 ! str1.equals(str2) Boolean<br />
Tabel 3.3: Standaardoperaties voor String type<br />
3.2.3 Vertaling van OCL collectieoperaties<br />
Voor we aan de vertaling van collectieoperaties beginnen moeten we eerst iets over OCL collectietypes<br />
zeggen. Deze OCL collectietypes moeten op de collectietypen van een doeltaal<br />
afgebeeld worden. Gelukkig biedt JAVA een brede waaier van collectietypes aan. Een mogelijke<br />
afbeelding is voorgesteld in tabel 3.4.<br />
OCL collectietype Java type Concreet Java type<br />
Set Set HashSet<br />
Sequence List ArrayList<br />
Bag List ArrayList<br />
OrderedSet List ArrayList<br />
Tabel 3.4: Afbeelding van OCL collectie typen naar JAVA collectie typen<br />
Een Set in OCL stelt een wiskundige verzameling voor. Deze Set bevat elementen zonder<br />
duplicaten. Een OrderedSet is een Set waarbij zijn elementen geordend zijn. Bovendien een<br />
OrderedSet bevat geen duplicaten.<br />
Een Bag is een collectie waarbij duplicaten wel toegelaten zijn. Met andere woorden kan een<br />
object meerdere keren een element van een Bag worden. In een Bag is er geen volgorde.<br />
Een Sequence is een collectie waarbij de elementen geordend zijn. Een element kan meer dan<br />
een keer deel van een Sequence maken.<br />
In OCL hebben we twee typen van operaties over collecties. Operaties die over collecties<br />
itereren en operaties die dat niet doen. De eerste soort noemt men collectieiteratoren en<br />
de tweede eenvoudige collectieoperaties. We zullen eerst de vertaling van de tweede soort<br />
beschrijven.
24 HOOFDSTUK 3. METHODOLOGIE<br />
3.2.3.1 Eenvoudige collectieoperaties<br />
De implementatie van eenvoudige collectieoperaties is meestal niet zo moeilijk. De meeste van<br />
deze operaties hebben een tegenhanger in de JAVA API over collectietypen. Indien dat niet<br />
het geval is zullen we voor deze operaties een aparte implementatie voorzien. Daarvoor zullen<br />
we een soort van sjablonen gebruiken. In tabel 3.5 tonen we de meest gebruikte standaard<br />
OCL operaties over alle collectietypen en hun JAVA tegenhangers.<br />
OCL operatie Java Tegenhanger Omschrijving<br />
excludes(object) ! contains(object) Geeft waar terug indien het object<br />
geen element van de collectie is<br />
excludesAll(collection) ! containsAll(collection) Geeft waar terug indien alle<br />
elementen van de gegeven<br />
collectie niet aanwezig<br />
in de huidige collectie zijn<br />
includes(object) contains(object) Geeft waar terug indien het object<br />
een element van de collectie is<br />
includesAll(collection) containsAll(collection) Geeft waar terug indien alle<br />
elementen van de gegeven<br />
collectie aanwezig in<br />
de huidige collectie zijn<br />
isEmpty() isEmpty() Geeft waar terug indien de collectie<br />
geen enkel element bevat<br />
notEmpty() ! isEmpty() Geeft waar terug indien de collectie<br />
een of meerdere elementen bevat<br />
size() size() Geeft het aantal elementen<br />
in de collectie terug<br />
Tabel 3.5: Standaardoperaties over alle soorten van collectie typen<br />
3.2.3.2 Collectieiteratoren<br />
Collectieiteratoren laten ons om over collecties te itereren. De collecties waarop deze operaties<br />
toegepast zijn noemen we hier source. Ieder element van de source wordt tegen een expressie<br />
geëvalueerd. Deze expressie noemen we body. In tabel 3.6 tonen we de collectieiteratoroperaties<br />
over alle collectietypen.<br />
Omdat de collectieiteratoren altijd over collecties itereren moet men deze iteraties in termen<br />
van de doeltaal genereren. Bijvoorbeeld, zijn iteraties meestal in JAVA geimplementeerd met<br />
behulp van de interface Iterator. We zullen hier namelijk deze constructie gebruiken om over<br />
collectie te kunnen itereren.<br />
De OCL collectieiteratoren (zie tabel 3.6) hebben geen tegenhangers in de JAVA Collection<br />
API. Bijgevolg moeten we voor elke collectieiterator een methode creëren. Hiervoor zullen we<br />
een soort van sjablonen gebruiken. In deze sjablonen zullen we een vaste en een variërende<br />
stukcode hebben. De variërende code zullen we met een bold-italic lettertype noteren.
3.2. BESCHRIJVING VAN DE METHODOLOGIE 25<br />
Operaties<br />
any(expr)<br />
collect(expr)<br />
collectNested(expr)<br />
exists(expr)<br />
forAll(expr)<br />
isUnique(expr)<br />
iterate(...)<br />
one(expr)<br />
reject(expr)<br />
select(expr)<br />
sortedBy(expr)<br />
Omschrijving<br />
Geeft een willekeurig element uit de source collectie<br />
terug waarvoor de expressie expr waar is<br />
Geeft een collectie van objecten terug als resultaat<br />
van de evaluatie van expr voor ieder element<br />
uit de source collectie<br />
Geeft een collectie van collecties terug als resultaat<br />
van de evaluatie van expr voor ieder element<br />
uit de source collectie<br />
Geeft waar terug als er minstens een element in<br />
de source collectie bestaat waarvoor expr waar is<br />
Geeft waar terug als expr voor alle elementen in<br />
de source collectie waar is<br />
Geeft waar terug als expr een unieke waarde voor alle<br />
elementen in de source collectie heeft<br />
Itereert over alle elementen in de source collectie<br />
Geeft waar terug als er juist een element in de source<br />
collectie is waarvoor expr waar is<br />
Geeft een deelcollectie van de source collectie terug<br />
waarvoor voor ieder element uit de source collectie<br />
expr niet waar is<br />
Geeft een deelcollectie van de source collectie terug<br />
waarvoor voor ieder element uit de source collectie<br />
expr waar is<br />
Geeft een collectie terug die alle elementen van de<br />
source collectie bevat en geordend volgens expr zijn.<br />
Tabel 3.6: Collectieiteratoren operaties over alle collectietypen<br />
any operatie<br />
Om eender welk element van een source collectie te verkrijgen die aan een bepaalde voorwaarde<br />
voldoet kunnen we de any operatie gebruiken. De body parameter van deze operatie is een<br />
booleaanse expressie. Deze operatie zal over alle elementen van de collectie itereren om een of<br />
meer elementen te vinden die aan de body expressie voldoen. Indien er meer dan een element<br />
is die aan de body conditie voldoet dan is willekeurig een van deze elementen teruggeven.<br />
In onze implementatie wordt het eerste element teruggegeven. Anders is het resultaat niet<br />
gedenieerd. De volgende OCL expressie representeert de any operatie met de bijbehorende<br />
body conditie die op een source collectie toegepast is.<br />
source->any(body)<br />
De JAVA code ziet er zo uit:
26 HOOFDSTUK 3. METHODOLOGIE<br />
Listing 5 Sjabloon voor OCL any operatie<br />
ElementType result = null;<br />
Iterator it = source.iterator();<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
if(elem.body){<br />
return elem;<br />
}<br />
}<br />
return result;<br />
Wat te merken is dat er weinig van de OCL code in de JAVA code terug te vinden is. Bovendien<br />
is er een expliciete vermelding van het type van de elementen in de collectie source nodig. In<br />
termen van JAVA noemt men dat casten. In dat fragment casten we naar elementen van type<br />
ElementType.<br />
one operatie<br />
De one operatie geeft een booleaans resultaat terug. Indien er juist een element in de source<br />
collectie is die aan de voorwaarde in de body voldoet geeft deze operatie als resultaat waar<br />
terug.<br />
source->one(body)<br />
De JAVA code ziet er zo uit voor de one operatie:<br />
Listing 6 Sjabloon voor OCL one operatie<br />
Iterator it = source.iterator();<br />
int nr = 0;<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
if(elem.body){<br />
nr++;<br />
}<br />
}<br />
if(nr != 1){<br />
return false;<br />
}<br />
return true;<br />
collect operatie<br />
De collect operatie itereert over een gegeven source collectie dan berekent een waarde voor<br />
elk element in de collectie en verzamelt vervolgens de geëvalueerde waarden in een nieuwe<br />
collectie. De elementen in de nieuwe collectie zijn niet noodzakelijk van hetzelfde type als de<br />
source collectie. Het resultaat van een collect operatie is altijd een vlakke collectie.
3.2. BESCHRIJVING VAN DE METHODOLOGIE 27<br />
source->collect(body)<br />
De JAVA code ziet er zo uit:<br />
Listing 7 Sjabloon voor OCL collect operatie<br />
List result = new ArrayList();<br />
Iterator it = source.iterator();<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
Object bodyObj = elem.body;<br />
if(bodyObj != null){<br />
result.add(bodyObj);<br />
}<br />
}<br />
return result;<br />
iterate operatie<br />
De iterate operatie is de meest fundamentele en ingewikkelde operatie die over collecties<br />
itereert maar bijzonder generisch. De operaties reject, select, forAll, exists, collect<br />
kunnen allemaal in termen van iterate beschrijven worden. De collect operatie beschreven<br />
in termen van iterate is als volgt:<br />
Listing 8 collect operatie in termen van iterate<br />
collection->collect(x:T|x.property)<br />
is identiek aan :<br />
collection->iterate(x:T; acc: T2= Bag{}|<br />
acc->including(x.property))<br />
In deze thesis hebben we geen sjabloon voor iterate voorgesteld. We vinden dat wat men met<br />
iterate wil uitdrukken kan in de meeste gevallen ook uitgedrukt worden met een specieke<br />
collectieiterator. Bovendien is veel makkelijker en overzichtelijker indien we een collectieiterator<br />
gebruiken dan gewoon de OCL iterate operatie bij run-time OCL. Indien we de OCL<br />
expressies van Listing 8 met elkaar vergelijken dan is duidelijk dat de eerste indrukking overzichtelijker<br />
is die de collectieiterator collect gebruikt. In werkelijkheid zal een implementatie<br />
van de iterate operatie op zichzelf niet zo veel werk vragen.<br />
exists operatie<br />
Meestal wil men speciceren dat er minstens een element in een collectie is die aan een bepaalde<br />
conditie voldoet. De exists operatie kan hiervoor gebuikt worden. Deze operatie geeft een<br />
booleaans resultaat terug. Met andere woorden is het resultaat van deze operatie waar indien<br />
er minstens een element in de collectie bestaat dat aan de voorwaarden gespeciceerd in de<br />
body van de operatie voldoet. Indien we de volgende OCL expressie hebben
28 HOOFDSTUK 3. METHODOLOGIE<br />
source->exists(body)<br />
dan is de corresponderende JAVA code in Listing 9 te zien. Hier source is een collectie en<br />
daarvoor gebruiken we de JAVA interface Iterator om over deze collectie te kunnen itereren.<br />
De JAVA code ziet er zo uit:<br />
Listing 9 Sjabloon voor OCL exists operatie<br />
Iterator it = source.iterator();<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
if(elem.body){<br />
return true;<br />
}<br />
}<br />
return false;<br />
forAll operatie<br />
Soms wil men dat er een beperking voor alle elementen uit een collectie geldt. In dat geval kan<br />
hij de forall operatie gebruiken. Indien de OCL expressie in de body van de operatie voor<br />
een of meer elementen uit de collectie faalt geeft de forall operatie niet waar als resultaat<br />
terug. Gegeven de volgende OCL expressie<br />
source->forall(body)<br />
De vertaling van de OCL expressie naar JAVA die de forall operatie bevat is te zien in de<br />
volgende sjabloon:<br />
Listing 10 Sjabloon voor OCL forAll operatie<br />
Iterator it = source.iterator();<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
if(! elem.body){<br />
return false;<br />
}<br />
}<br />
return true;<br />
isUnique operatie<br />
Indien men in een collectie van elementen een unieke waarde voor ieder element in deze<br />
collectie wenst moet hij de isUnique operatie gebruiken. De body van deze operatie is meestal<br />
een expressie over een bepaalde eigenschap van het type van de betrokkene elementen in de<br />
collectie. Het resultaat van deze operatie geeft een booleaanse waarde terug. Deze operatie zal<br />
over alle elementen itereren en zal voor elk element de gevraagd waarde met de meegegeven
3.2. BESCHRIJVING VAN DE METHODOLOGIE 29<br />
body waarde vergelijken. Indien geen enkel van deze waarden aan elkaar gelijk zijn geeft deze<br />
operatie als resultaat waar terug. Voor de volgende OCL expressie hebben we<br />
source->isUnique(body)<br />
De JAVA code ziet er zo uit:<br />
Listing 11 Sjabloon voor isUnique operatie<br />
Iterator it = source.iterator();<br />
List values = new ArrayList();<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
if(values.contains(body)){<br />
return false;<br />
}<br />
values.add(body);<br />
}<br />
return true;<br />
select operatie<br />
Zowel associaties als resultaten uit operaties kunnen een collectie teruggeven. Indien men in<br />
een deelverzameling van deze collectie geïnteresseerd is kan hij de select operatie gebruiken.<br />
Met andere woorden kan men in de body van deze operatie een lijst van criteria speciceren<br />
om een selectie te maken. Bijgevolg is de body van de select operatie een booleaanse expressie.<br />
Het resultaat van deze operatie resulteert in een deelverzameling van de oorspronkelijke<br />
collectie. In deze deelverzameling zijn alleen maar elementen uit de oorspronkelijke collectie<br />
geselecteerd die aan de voorwaarden van de body voldoen.<br />
Gegeven de volgende OCL expressie, is source een collectie en body een booleaanse expressie.<br />
De corresponderende JAVA sjabloon is hieronder getoond.<br />
source->select(body)<br />
De vertaling van de select operatie naar JAVA is als volgt:<br />
Listing 12 Sjabloon voor OCL select operatie<br />
Set result = new HashSet();<br />
Iterator it = source.iterator();<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
if(elem.body){<br />
result.add(elem);<br />
}<br />
}<br />
return result;
30 HOOFDSTUK 3. METHODOLOGIE<br />
reject operatie<br />
De reject operatie is analoog aan de select operatie met het enige verschil dat allen elementen<br />
uit de source collectie geselecteerd moet worden waarvoor de expressie in de body van de<br />
operatie tot niet waar moet evalueren.<br />
source->reject(body)<br />
Hieronder geven we de JAVA vertaling van de OCL reject expressie.<br />
Listing 13 Sjabloon voor OCL reject operatie<br />
Set result = new HashSet();<br />
Iterator it = source.iterator();<br />
while(it.hasNext()){<br />
ElementType elem = (ElementType) it.next();<br />
if(!elem.body){<br />
result.add(elem);<br />
}<br />
}<br />
return result;<br />
sortedBy operatie<br />
De operatie sortedBy resulteert in een gesorteerde verzameling. Het element in de body van<br />
de operatie die de laagste waarde heeft komt als eerste element enz. Het type van de body<br />
expressie moet de < operatie denieren die een booleaanse waarde moet teruggeven. Indien<br />
we de volgende OCL expressie hebben<br />
source->sortedBy(body)<br />
De JAVA code ziet er zo uit:<br />
Listing 14 Sjabloon voor OCL sortedBy operatie<br />
List result = new ArrayList();<br />
result.addAll(source);<br />
Comparator comp = new ComparatorElementTypebody();<br />
Collections.sort(result,comp);<br />
return result;<br />
Combinatie van collectieiteratoren<br />
Het lichaam van een collectieoperatie kan veel complexer in vergelijking met de vorige voorbeelden<br />
uitzien wanneer we een combinatie van collectieiteratoren nemen. In zulke gevallen<br />
wordt onze JAVA code heel wat ingewikkeld en moeilijk om de OCL code te herkennen. We<br />
gaan dat aantonen aan de hand van een voorbeeld.
3.2. BESCHRIJVING VAN DE METHODOLOGIE 31<br />
source1->select(source2->forall(body))<br />
In deze OCL expressie hebben we met twee collecties te maken. De ene die over source1<br />
itereert en de andere die over source2 itereert. In OCL is wel mogelijk dat we meerdere<br />
geneste operaties over collecties kunnen hebben alhoewel dat uitzonderlijk is. De vertaalde<br />
JAVA code is als volgt:<br />
Listing 15 Sjabloon voor combinatie van collectieiteratoren<br />
Iterator it1 = source1.iterator();<br />
Set result = new HashSet();<br />
while(it1.hasNext()){<br />
Source1 s1 = (Source1) it.next();<br />
Iterator it2 = s1.source2.iterator();<br />
boolean forAllRes = true;<br />
while(it2.hasNext()){<br />
Source2 s2 = (Source2) it2.next();<br />
forAllRes = forAllRes && (s2.body);<br />
}<br />
if(forAllRes ){<br />
result.add(s1);<br />
}<br />
}<br />
return result;<br />
We kunnen samenvatten dat iedere collectieoperatie over een sjabloon moet beschikken. Deze<br />
sjabloon moet vervolgens ingevuld worden met de elementen van de OCL expressies. Er<br />
mogen nog wat optimalisaties over de sjablonen gemaakt worden. Natuurlijk resulteert dat in<br />
ingewikkelde JAVA code. Bijvoorbeeld, de sjabloon van hierboven kan herschrijven worden.<br />
Met andere woorden moet de iteratie stopen zodra het resultaat fout is.<br />
Een andere oplossing voor geneste collectieiteratoren is om gewoon de voorgestelde sjablonen<br />
voor elke collectieiterator te gebruiken. In ons voorbeeld van daarboven passen we eerst de<br />
sjabloon voor de select operatie toe. Vervolgens roepen we de sjabloon voor de forAll operatie<br />
op in de plaats waarin we de body van de select operatie nagaan. Op deze manier<br />
moeten we geen nieuwe sjablonen voor geneste collectieiteratoren denieren die wat gecompliceerd<br />
kunnen zijn. Als resultaat verkrijgen we in dit voorbeeld twee in plaats van een hulp<br />
methoden. Daarenboven zijn de verkregen hulp methoden overzichtelijk en eenvoudig.<br />
Met deze oplossing reduceren we niet alleen de complexiteit van onze sjablonen maar ook<br />
het aantal sjablonen die in elk geval gedenieerd moeten worden door een hergebruik van al<br />
bestaande sjablonen te maken.<br />
3.2.3.3 Inkapseling van sjablonen voor collectieoperaties in JAVA methoden<br />
De daarnet beschreven sjablonen voor OCL collectieoperaties moeten op een of andere manier<br />
in JAVA methoden ingekapseld worden. Hieronder geven we een aantal mogelijke oplossingen<br />
voor dit probleem.
32 HOOFDSTUK 3. METHODOLOGIE<br />
Een eerste oplossing die wie hier voorstellen is als volgt: telkens we een collectieoperatie<br />
tegenkomen dan gaan we een hulpmethode voor deze genereren in de klasse (context) waarbij<br />
deze hoort. In deze context zijn hulpmethode OCL collectieoperaties die geen tegenhangers<br />
in JAVA Collection API hebben. Bijgevolg is er de mogelijkheid van gedupliceerde methoden<br />
met dezelfde inhoud niet uitgesloten.<br />
Een tweede oplossing zou kunnen zijn om een keer een standaard bibliotheek te genereren die<br />
alle hulpoperaties zal bevatten die geen tegenhangers in de JAVA API hebben. Vermits onze<br />
sjablonen zowel een vast als een variabel deel hebben, moeten we in de signatuur van deze<br />
operaties als argumenten de source collectie en de body expressie meegeven om het variabele<br />
gedeelte in te vullen. Dit lijkt een goede oplossing maar aan de andere hand moeilijk te<br />
implementeren.<br />
Een derde oplossing zou kunnen zijn om in plaats van een standaard bibliotheek met statische<br />
methoden te genereren nemen we de eerste oplossing en passen we paar verbeteringen erop toe.<br />
Voor we een hulpoperatie gaan genereren, gaan we eerst nagaan of al deze niet gegenereerd<br />
was. Indien dat het geval was, moeten we deze niet meer gaan genereren. Om deze aanpak<br />
te kunnen implementeren, moeten we ergens alle onze gegenereerde hulpoperaties met hun<br />
bijbehorende source collectie en de body expressies bijhouden.<br />
In deze thesis gebruiken we de eerste aanpak. De uiteindelijke geproduceerde code zorgt voor<br />
heel wat duplicatie van methoden bij gebruik van collectieiteratoren. Indien dat door een<br />
programmeur handgeschreven is kan dat heel wat lastig zijn en tot fouten leiden. Vermits de<br />
code door een tool gegenereerd is, is deze code replicatie niet een probleem. Het enige nadeel<br />
is de grootte van de geproduceerde code. In alle systemen behalve embedded systemen is dat<br />
geen probleem. In embedded systemen waarin de geheugengrootte beperkt is is deze aanpak<br />
minder geschikt. Maar dit ligt buiten het bereik van onze thesis.<br />
3.2.4 Vertaling van OCL if-then-else expressies<br />
Er zijn twee mogelijkheden voor de vertaling van de OCL if-then-else constructie naar<br />
JAVA. Ofwel gebruiken we de corresponderende if-then-else constructie in JAVA ofwel de<br />
korte notatie voor deze constructie. Als voorbeeld geven we de volgende OCL if-then-else<br />
expressie.<br />
Listing 16 Een voorbeeld van een OCL if-then-else expressie<br />
if <br />
then <br />
else <br />
endif<br />
In dit eenvoudige voorbeeld hebben we geen collectieoperaties (collectieiteratoren) opgenomen<br />
maar indien dat het geval was dan zouden we extra hulpmethoden genereren. De JAVA code<br />
maakt gebruik van de verkorte notatie van de if-then-else constructie ook bekend als de<br />
ternaire operator ( ? : ). We gebruiken toch de verkorte notatie van de if-then-else<br />
constructie want deze past eleganter in onze sjablonen.
3.2. BESCHRIJVING VAN DE METHODOLOGIE 33<br />
Listing 17 Sjabloon OCL if-then-else expressie<br />
return (< translation of boolean OCL expression >) ?<br />
: ;<br />
3.2.5 Vertaling van OCL let expressies<br />
In OCL let expressies zijn er lokale variabelen gedenieerd. De implementatie van de let<br />
expressie in JAVA gebeurt op dezelfde wijze. In de sjabloon voor de let expressie worden<br />
deze lokale variabelen ook gedenieerd. Er moet wel aandacht aan de scope van de lokale<br />
variabelen besteed worden. De typen van deze variabelen in de OCL let expressie moeten op<br />
de juiste JAVA typen afgebeeld worden. Dat is het variabel gedeelte voor de let sjabloon. In<br />
de JAVA code is dat te zien met bold-italic lettertypen.<br />
Listing 18 Een voorbeeld van een OCL let expressie<br />
let<br />
tmp1 : Integer = self.attribute1,<br />
tmp2: String = self.attribute2<br />
in<br />
if self.attr3 then<br />
tmp1 >= 18<br />
else tmp1 < 18<br />
endif<br />
De bovenstaande OCL let-expressie illustreert hoe de vertaling naar JAVA gebeurt. Er<br />
kunnen ook nog collectieiteratoren in het 'let' of 'in' deel van de let expressie voorkomen.<br />
In dit geval worden hulpmethoden voor deze collecties gegenereerd.<br />
Listing 19 Sjabloon voor OCL let expressie<br />
boolean result = false;<br />
int tmp1 = this.getAttribute1() ;<br />
String tmp2 = this.get Attribute2() ;<br />
result = (this.getA ttr3() ? tmp1 >= 18 : tmp1 < 18;<br />
return result;<br />
3.2.6 Vertaling van klasse-invarianten<br />
Elke invariant wordt vertaald in een publieke booleaanse methode. Bij de naam van deze<br />
booleaanse methoden wordt de naam van de invariant geconcateneerd. Op deze manier is er<br />
een garantie voor consistentie tussen de verschillende gegenereerde invarianten. De publieke<br />
visibiliteit van de methode zorgt voor dat instantiaties van de klasse waarin deze methode<br />
gedenieerd is een toegang tot deze methode krijgen. Hieronder tonen we een sjabloon van<br />
een methode voor een klasse-invariant die een vast en een variabel gedeelte heeft.
34 HOOFDSTUK 3. METHODOLOGIE<br />
Listing 20 Sjabloon voor inkapseling van OCL invariant expressie in JAVA methode<br />
public boolean checkInvNameOfInvariant(){<br />
return < translation of inv OCL expressie >;<br />
}<br />
OCL expressies mogen constructies bevatten die hulpmethoden vereisten. Voorbeeld van zulke<br />
constructies hebben we al in de vorige sectie gezien, namelijk operaties die over collecties<br />
itereren. In dit geval worden een of meerdere hulpmethoden gegenereerd. Hier moeten we ook<br />
voor unieke namen van deze hulpmethoden zorgen. Dezelfde benadering als bij de namen van<br />
klasse-invarianten is genomen om het probleem van name clashes op te lossen.<br />
Dan zal onze sjabloon er als volgt uitzien.<br />
Listing 21 Sjabloon voor inkapseling van OCL invariant expressie in een JAVA methode<br />
public boolean checkInvNameOfInvariant(){<br />
return typeOfOCLExpressionNameOfInvariant();<br />
}<br />
private type typeOfOCLExpressionNameOfInvariant(){<br />
< template for the corresponding OCL expression >;<br />
}<br />
Met typeOfOCLExpression bedoelen we het type van een OCL expressie. Bijvoorbeeld dat<br />
kan een collectieiterator zijn (forall, exists, ...) of een let expressie of een andere OCL<br />
expressie zijn.<br />
3.2.7 Vertaling van pre- en postcondities<br />
De vertaling van pre- en postcondities heeft een aantal gelijkenissen met de vertaling van<br />
klasse-invarianten: elke pre- en postconditie zal in een aparte booleaanse methode ingekapseld<br />
worden. Er zullen nog hulpmethoden gegenereerd worden indien dit nodig zijn.<br />
Natuurlijk moeten we niet vergeten dat pre- en postcondities altijd bij operaties horen. Bijgevolg<br />
moeten ze bij het lichaam van de operatie zelf ingevoegd worden. Voor precondities is<br />
dat in het begin van de operaties, juist voor het uitvoeren van deze en voor postcondities is<br />
dat juist na het uitvoeren van de operatie.<br />
De sjabloon is hieronder te zien:
3.2. BESCHRIJVING VAN DE METHODOLOGIE 35<br />
Listing 22 Sjabloon voor pre- en postcondities<br />
public boolean checkPreNameOfPreconditionNameOfOperation(){<br />
return ;<br />
}<br />
public boolean checkPostNameOfPostconditionNameOfOperation(){<br />
return ;<br />
}<br />
operation(){<br />
assert checkPreNameOfPreconditionNameOfOperation();<br />
//<br />
// body of the operation<br />
//<br />
assert checkPostNameOfPostconditionNameOfOperation();<br />
//return statement if not void type<br />
}<br />
Klasse-invarianten zijn niet expliciet in operaties opgenomen maar ze moeten toch nagegaan<br />
worden na het uitvoeren van publieke methoden en constructoren. We hebben nog niet getoond<br />
hoe we dat zullen doen. In sectie 3.3 en 3.4 zullen we dat grondig bespreken.<br />
3.2.8 Vertaling van meer geavanceerde constructies<br />
3.2.8.1 Vertaling van het sleutelwoord @pre<br />
Het sleutelwoord @pre representeert de waarde van een attribuut of associatie op het begin<br />
van de uitvoering van een operatie. Dit sleutelwoord moet na de naam van het betrokken<br />
attribuut of associatie vermeld worden. Vermits hier duidelijk te zien is dat we met variabelen<br />
over tijd te maken hebben, moeten tijdelijke variabelen in de vertaalde code voorzien worden.<br />
Een voorbeeld is de volgende OCL postconditie in de context van een operatie operation die<br />
tot de klasse Class behoort.<br />
context Class :: operation(attribute2 : Integer)<br />
post p1:<br />
attribute = attribute@pre + attribute2<br />
Bij de vertaling naar JAVA moeten we een tijdelijke variabele denieren die de waarde van het<br />
attribuut attribute bijhoudt. We zullen deze variabele old_attribute noemen. Old_attribute<br />
moet een kloon of kopie zijn maar geen kopie van de referentie want de gerefereerde waarde<br />
zal tijdens het uitvoeren van de operatie veranderen.
36 HOOFDSTUK 3. METHODOLOGIE<br />
Listing 23 Sjabloon voor OCL @pre expressie<br />
void operation(int attribute2){<br />
int old_attr = this.getAttribute();<br />
// ...<br />
// <br />
// ...<br />
assert checkPostP1Operation(old_attr,attribute2);<br />
}<br />
public boolean checkPostP1Operation(int old_attr, int i){<br />
return this.getAttribute() == old_attr + i;<br />
}<br />
Indien we tijdelijke objecten willen bijhouden gebruiken we dan een kopie-constructor. Kopieconstructors<br />
moeten dan in de klasse waarin de betrokken operatie behoort gedenieerd worden.<br />
Vervolgens mogen we de sjabloon van daarvoor toepassen. De JAVA code tonen we<br />
hieronder:<br />
Listing 24 @pre object<br />
void operation(Object object2){<br />
Object old_obj = new Object(obj);<br />
// ...<br />
// <br />
// ...<br />
assert checkPostP1Operation(old_obj,object2);<br />
}<br />
public boolean checkPostP1Operation(Object old_obj, Object object2){<br />
return this.getObject().getProperty() ==<br />
old_obj.getProperty() + object2.getProperty();<br />
}<br />
3.2.8.2 Het sleutelwoord result<br />
Het sleutelwoord result geeft de terugwaarde van een operatie terug indien er een is. Bijgevolg<br />
is het type van result bepaald door het terugtype van de operatie. Dit sleutelwoord wordt<br />
gebruikt bij postcondities van operaties.<br />
Listing 25 Een voorbeeld van het OCL result sleutelwoord<br />
context Class :: operation(attribute2 : Integer):Integer<br />
post p2: result = attribute2*1000<br />
De resulterende JAVA code is:
3.3. NAÏEVE AANPAK 37<br />
Listing 26 Sjabloon voor OCL result sleutelwoord<br />
int operation(int attribute2){<br />
int result = 0;<br />
// ...<br />
// <br />
// ...<br />
assert checkPostP2Operation(result,attribute2);<br />
return result;<br />
}<br />
public boolean checkPostP2Operation(int result,int i){<br />
return result == this.getAttribute2()*1000;<br />
}<br />
3.3 Naïeve aanpak<br />
In deze sectie illustreren we de meest evidente aanpak. In deze aanpak worden klasseinvarianten,<br />
pre- en postcondities ingekapseld in booleaanse methoden. Klasse-invarianten<br />
moeten waar zijn op ieder consistent moment. Met andere woorden zijn klasse-invarianten<br />
gecontroleerd voor en na elke uitvoering van publieke methodes en constructoren. Alle klasseinvarianten<br />
zijn verder in een gemeenschappelijke booleaanse methode ingekapseld door een<br />
conjunctie van deze invarianten te nemen. Pre- en postcondities worden respectievelijk voor<br />
en na de executie van de publieke methoden en constructoren nagegaan.<br />
3.3.1 Vertaling van invarianten<br />
In de vorige sectie hebben we een beschrijving van een mogelijke vertaling van OCL expressies<br />
naar JAVA gezien. Nu kunnen we deze methodologie op klasse-invarianten in een model<br />
toepassen.<br />
3.3.1.1 Inkapseling van een invariant in een booleaanse methode<br />
De inkapseling van klasse-invarianten en de bijbehorende sjablonen waren al in de vorige<br />
sectie besproken. Hier zullen we meer uitgebreid deze vertaling bespreken. Stel voor dat we<br />
een klasse met een aantal klasse-invarianten hebben:<br />
Listing 27 voorbeeld<br />
context ClassA<br />
inv invariant_1: <br />
inv invariant_2: <br />
inv invariant_3: -> forall()<br />
De vertaling van deze klasse-invarianten naar JAVA is hieronder gegeven. Elke invariant is<br />
vertaald in een booleaanse methode. Bij de naam van elke van deze booleaanse methoden is<br />
de naam van de invariant geconcateneerd. Op deze manier is er een garantie voor consistentie<br />
tussen de verschillende gegenereerde invarianten. In invariant_3 zien we dat we met een
38 HOOFDSTUK 3. METHODOLOGIE<br />
collectieiterator (forall operatie) te maken hebben. In dit geval is een aparte methode<br />
gegenereerd waarin alle bewerkingen omtrent deze invariant ingekapseld zijn.<br />
Listing 28 Sjabloon vertaling van klasse invarianten<br />
public boolean checkInvInvariant1(){<br />
return ;<br />
}<br />
public boolean checkInvInvariant2(){<br />
return ;<br />
}<br />
public boolean checkInvInvariant3(){<br />
return forallInvariant3();<br />
}<br />
private boolean forallInvariant3(){<br />
<br />
}<br />
3.3.1.2 Inkapseling van alle klasse-invarianten in een gemeenschappelijke methode<br />
Eens we een implementatie voor alle invarianten hebben kunnen we deze in een gemeenschappelijke<br />
booleaanse methode inkapselen. Hierin geven we een conjunctie van alle invarianten<br />
terug. Deze methode zal vervolgens na iedere afhandeling van een publieke constructor of<br />
methode opgeroepen worden. Dat zal gebeuren door de assert statement die we in het begin<br />
van dit hoofdstuk besproken hebben. Op deze manier controleren we altijd alle invarianten.<br />
Dat levert een overhead van testen die overbodig kunnen zijn. We zullen een meer geavanceerde<br />
aanpak in volgende sectie bespreken. Hieronder tonen we de JAVA code die alle OCL<br />
invariant-expressies in een gemeenschappelijke methode inkapselt.<br />
Listing 29 Sjabloon checkAllInvariants<br />
public boolean checkAllInvariants(){<br />
return checkInvInvariant1()&&<br />
checkInvInvariant2()&&<br />
checkInvInvariant3();<br />
}<br />
3.3.2 Vertaling van pre- en postcondities<br />
Tot nu hebben we gezien hoe klasse-invarianten vertaald kunnen worden. Hier gaan we bespreken<br />
hoe de vertaling van de pre- en postcondities naar JAVA gebeurt. Pre- en postcondities<br />
horen altijd bij operaties. Bijgevolg zullen we deze in de operaties implementeren waarin ze<br />
gedenieerd zijn. Het mechanisme dat we hier gaan gebruiken is de JAVA assert. Laat ons<br />
veronderstellen dat we een operatie operation1 in de context van een klasse ClassA hebben.<br />
Deze operatie heeft twee precondities en een postconditie. De OCL code is hieronder gegeven:
3.3. NAÏEVE AANPAK 39<br />
Listing 30 voorbeeld operatie met pre- en postcondities<br />
context ClassA :: operation1() : <br />
pre pre1: <br />
pre pre2: <br />
post post1: <br />
Voor elke pre- en postconditie zullen we een aparte booleaanse methode genereren. Daarna<br />
voegen we de pas gegenereerde pre- en postconditiemethoden in in de juiste plaatsen van onze<br />
operatie. Voor precondities is dat in het begin van de operatie. Voor postcondities is dat op<br />
het einde van deze operatie. Het invoegen van deze code gebeurt door de assert constructie<br />
die we in het begin van dit hoofdstuk besproken hebben.<br />
De naam van elke pre- en postconditiemethode is geconcateneerd met de naam van de preof<br />
postconditie die ze voorstellen en met de naam van de operatie waartoe ze behoren. Dat<br />
zorgt voor unieke namen van methoden en lost het probleem van name clashes op.<br />
De JAVA code ziet er als volgt uit:<br />
Listing 31 Sjabloon voor pre- en post-condities bij de naïeve aanpak<br />
operation1(){<br />
assert(checkAllInvariants());<br />
assert(checkPrePre1Operation1());<br />
assert(checkPrePre2Operation1()) ;<br />
//<br />
// body of operation<br />
//<br />
assert(checkPostPost1Operation1());<br />
assert(checkAllInvariants());<br />
}<br />
private boolean checkPrePre1Operation1(){<br />
return ;<br />
}<br />
private boolean checkPrePre2Operation1(){<br />
return ;<br />
}<br />
private boolean checkPostPost1Operation1(){<br />
return ;<br />
}<br />
We moeten ook niet vergeten dat bij operaties klasse-invarianten ook gecontroleerd moeten<br />
worden alhoewel ze niet opgenomen zijn in de operatie. De controle op invarianten moet<br />
voor en na de uitvoering van elke publieke methode en na de uitvoering van elke constructor<br />
gebeuren. Bijgevolg moeten we een assertie van alle invarianten toevoegen. Dat is te zien in<br />
de eerste en laatste regel van de JAVA vertaling voor de operation1() methode.
40 HOOFDSTUK 3. METHODOLOGIE<br />
3.4 Intelligente aanpak<br />
De aanpak die we in de vorige sectie voorgesteld hebben is de meest intuïtieve aanpak. Voor<br />
elke uitvoering van een publieke operatie en na elke uitvoering van een publieke operatie of<br />
constructor toetsen we alle klasse-invarianten. We doen dat door een conjunctie van deze<br />
invarianten te nemen.<br />
In deze sectie stellen we een intelligentere aanpak voor. In plaats van alle klasse-invarianten<br />
te controleren na het uitvoeren van een publieke operatie gaan we alleen maar de relevante<br />
klasse-invariante testen.<br />
3.4.1 Het Idee<br />
De condities die in de klasse-invarianten vastgesteld zijn moeten altijd voldaan voor alle instantiaties<br />
zijn waarvoor deze voorwaarden gedenieerd zijn. Wanneer een instantiatie van<br />
toestand verandert door bijvoorbeeld een publieke operatie erop toe te passen dan moeten<br />
alle condities in de klasse-invarianten ook voldaan zijn. Echter betrekken operaties vaak niet<br />
alle condities van klasse-invarianten op de toegepaste instantiaties. Bijgevolg is de controle<br />
van alle klasse-invarianten overbodig. Bovendien is op deze manier de uitvoeringstijd van<br />
applicaties verhoogd. Een reductie van controles op de geldigheid van klasse-invarianten en<br />
uitvoeringstijd van applicaties is wat we met de intelligente aanpak willen bereiken.<br />
We gaan nu een constructie voorstellen voor de implementatie van een intelligente aanpak.<br />
Binnen deze intelligente aanpak hebben we twee mogelijke implementaties bedacht. De ene is<br />
door een graaf te construeren en de andere is op string matching gebaseerd. We sluiten hierbij<br />
niet uit dat er nog andere manieren zijn om de intelligente aanpak te kunnen implementeren.<br />
3.4.2 Graafconstructie<br />
Het idee van deze aanpak is om een twee-stap graaf te construeren. We willen een afhankelijkheid<br />
tussen invarianten en postcondities van operaties bouwen. In deze constructie hebben<br />
we twee stappen. De eerste stap noemen we post-code generatie en de tweede stap noemen<br />
we pre-compilatie. Bij de eerste stap kijken we naar de postcondities van onze methoden. Bij<br />
de tweede stap kijken we naar de implementaties van onze methoden.<br />
Bij de post-code generatie stap controleren we of de toestand van een variabele of object<br />
veranderd is. Om dat te kunnen doen, doen we beroep op de postcondities van onze operaties<br />
en de klasse-invarianten. Op deze manier kunnen we beslissen welke invarianten na<br />
het uitvoeren van de operatie nagegaan moeten worden. Hierbij zullen niet alle betrokken<br />
objecten of variabelen van toestand veranderen. Bijgevolg verwachten we dat de gegenereerde<br />
verzameling van relevante klasse-invarianten kleiner is.<br />
Indien onze operaties geen postcondities bevatten dan stappen we naar de tweede stap over,<br />
namelijk de pre-compilatie stap.<br />
Bij de pre-compilatie stap kijken we naar alle referenties van de objecten of variabelen betrokken<br />
bij de implementatie van de methode. Vervolgens zoeken we in welke klasse-invarianten<br />
deze variabelen te vinden zijn. Op deze manier selecteren we een deelverzameling van alle
3.4. INTELLIGENTE AANPAK 41<br />
Figuur 3.1: Afhankelijkheid tussen klasse invarianten en postcondities horend bij operaties<br />
klasse-invarianten. Deze deelverzameling is in ieder geval kleiner of gelijk dan de oorspronkelijke<br />
verzameling van alle klasse-invarianten.<br />
Kijken in de broncode van de operatie (pre-compilatie stap) voor variabelen die van toestand<br />
veranderen kan soms lastig zijn. Indien een operatie een andere operatie oproept en deze op<br />
zijn beurt een derde operatie oproept dan ontstaat er een geneste oproep van operaties die tot<br />
een serie van complexe testen leidt.<br />
Laat ons dit uitleggen aan de hand van een klein voorbeeld. Ons voorbeeld heeft drie klasseinvarianten<br />
en een operatie met een postconditie.<br />
Listing 32 voorbeeld<br />
context Class<br />
inv inv1: age > 18<br />
inv inv2: firstName->size()->notEmpty()<br />
inv inv3: lastName->size()->notEmpty()<br />
context Class :: method2()<br />
post post1: lastName = firstName + 'Jr'<br />
In de post-code generatie stap gaan we naar variabelen of objecten kijken die van toestand<br />
veranderen. In ons voorbeeld verandert allen maar de variabele lastName van toestand. Bijgevolg<br />
moeten we alleen inv3 controleren. De corresponderende JAVA code is hieronder<br />
gegeven:
42 HOOFDSTUK 3. METHODOLOGIE<br />
Listing 33 sjabloon post-code<br />
method2(){<br />
//<br />
// body of operation<br />
//<br />
assert(checkPostPost1Method2());<br />
assert(checkInvInv3());<br />
}<br />
In de pre-compilatie stap is de verzameling van klasse-invarianten die gecheckt moeten worden<br />
groter. Hier moeten we naar alle variabelen die betrokken (gerefereerd) zijn in de implementatie<br />
van deze operatie gaan kijken. De variabele betrokken bij inv1 wordt niet gerefereerd<br />
in deze operatie. Daarom beschouwen we deze niet. De variabelen lastName en rstName<br />
worden wel gerefereerd. Deze zijn in inv2 en inv3 betrokken en bijgevolg moeten we deze<br />
twee invarianten controleren. De gegenereerde JAVA code zou zo eruitzien:<br />
Listing 34 sjabloon pre-compilatie<br />
method2(){<br />
//<br />
// body of operation<br />
//<br />
assert(checkPostPost1Method2());<br />
assert(checkInvInv2());<br />
assert(checkInvInv3());<br />
}<br />
3.4.3 String matching<br />
String matching is een lichte versie van de graafconstructie aanpak. Bij deze aanpak wordt geen<br />
expliciete graaf opgebouwd. Hierbij maken we gebuikt van reguliere expressies. Vervolgens<br />
passen we deze reguliere expressies op de postcondities van de operaties toe. Met andere<br />
woorden om te bepalen of een variabele of object in een postconditie van toestand veranderd<br />
is, moet deze aan de linkerkant van een toekenningsoperator zitten of moeten er operaties erop<br />
uitgevoerd worden. Vervolgens wordt het relevante object of variabele (die in een postconditie<br />
van toestand verandert) in alle klasse-invarianten opgezocht. Indien deze in een van de klasseinvariante<br />
betrokken is, dan is deze invariant geselecteerd (toegevoegd aan de verzameling van<br />
relevante klasse-invarianten).<br />
3.4.4 Vertaling van klasse-invarianten<br />
De vertaling van klasse-invarianten is gebaseerd op de sjablonen die we in sectie 3.2 voorgesteld<br />
hebben. Vervolgens zijn deze sjablonen in booleaanse methoden ingekapseld. In subsectie<br />
3.4.4.1 gaan we dat bespreken.
3.5. EVALUATIE 43<br />
3.4.4.1 Inkapseling van een invariant in een booleaanse methode<br />
Inkapseling van klasse-invarianten bij het gebruik van de intelligente aanpak verschilt niet<br />
van de inkapseling van klasse-invarianten bij het gebruik van de naïeve aanpak. Elke klasseinvariant<br />
is in een booleaanse methode ingebakken. Voor meer informatie verwijzen we naar<br />
sectie 3.3.1.1<br />
Wat hier meer aandacht vereist is de inkapseling van de relevante klasse-invarianten in een<br />
gemeenschappelijke booleaanse methode. Dat gaan we hieronder bespreken.<br />
3.4.4.2 Inkapseling van alle relevante klasse-invarianten in een gemeenschappelijke<br />
booleaanse methode<br />
Bij de naïeve aanpak hebben we gezien dat alle klasse-invarianten in een gemeenschappelijke<br />
methode ingepakt waren door een conjunctie van deze te nemen.<br />
Een mogelijke oplossing voor de intelligente aanpak is opnieuw een conjunctie van de relevante<br />
klasse-invarianten te nemen die vervolgens in een gemeenschappelijke methode ingekapseld<br />
worden. Het bepalen van de verzameling van relevante klasse-invarianten hangt af van de<br />
postconditie of de implementatie van een publieke methode. Wat een nadeel hier kan zijn<br />
is dat we telkens voor elke operatie een gemeenschappelijke methode zullen hebben die een<br />
conjunctie van de bijbehorende relevante klasse-invarianten zou bevatten. Daarom stellen we<br />
hieronder een andere oplossing voor.<br />
Een tweede oplossing is om in plaats van een conjunctie van alle relevante klasse-invarianten<br />
te nemen, voegen we elke relevante klasse-invariant op het einde van een methode toe door<br />
gebruik van de assert constructie te maken. Met andere woorden zijn de relevante klasseinvarianten<br />
bij een operatie niet in een gemeenschappelijke methode ingepakt. Op deze manier<br />
verwijderen we de overhead van generatie van methoden die de geldigheid van klasseinvarianten<br />
voor elke operatie gaan controleren.<br />
3.4.5 Vertaling van pre- en postcondities<br />
Voor de vertaling van pre- en postcondities van een methode verwijzen we naar 3.3.2. Het<br />
enige verschil dat hier zou kunnen zijn is dat de gemeenschappelijke methode omtrent het<br />
nagaan van klasse-invarianten op basis van subsectie 3.4.4.2 zou gegenereerd worden. Met<br />
andere woorden kunnen we ofwel een conjunctie van de relevante klasse-invarianten nemen<br />
ofwel de relevante klasse-invarianten afdrukken door de assert constructie te gebruiken.<br />
3.5 Evaluatie<br />
In deze sectie zullen we de naïeve en de intelligente aanpak evalueren in functie van uitvoeringstijd.<br />
Om dat te kunnen doen hebben we een klein lopend voorbeeld bedacht (zie Figuur<br />
3.1). Daarna hebben we manueel de code met de bijbehorende beperkingen voor de naïeve en<br />
de intelligente aanpak gemodelleerd.
44 HOOFDSTUK 3. METHODOLOGIE<br />
Wat wij met dit voorbeeld willen meten is de verhouding van de twee aanpakken ten opzichte<br />
van elkaar. In de naïeve aanpak hebben we op iedere uitvoering van een publieke methode<br />
alle gedenieerde klasse-invarianten getest. Bij de intelligente aanpak hebben we maar een<br />
deelverzameling van alle klasse-invarianten gecontroleerd. Deze deelverzameling is afhankelijk<br />
van de opgeroepen operatie.<br />
Ons lopend voorbeeld bestaat uit vijf klassen met een totaal van 21 klasse-invarianten.<br />
Om betrouwbare uitvoeringstijd te verkrijgen hebben we de testen 100000 keer in een lus laten<br />
draaien. Op deze manier verkrijgen we een acceptabele meting voor onze testverzameling.<br />
Het experiment wordt uitgevoerd op een PC 1.4 Ghz machine met 512 MB RAM geheugen<br />
en Windows XP als besturingssysteem.<br />
De resultaten wijken niet veel van onze veronderstellingen af. We hebben een verschil in<br />
uitvoeringstijd van 1.6 keer meer bij de naïeve aanpak dan bij de intelligente aanpak gemeten<br />
op voorwaarde dat onze methoden geen computationeel intensieve implementaties bevatten.<br />
De gemeten factor van 1.6 kan sterk variëren. In ons experiment is het nagaan van de klasseinvarianten<br />
de dominante operatie in een methode en vandaar komt deze versnelling tot 40<br />
percent bij de intelligente aanpak. Indien onze methoden computationeel intensieve algoritmes<br />
bevatten of grote bestanden aan lezen of wegschrijven zijn dan wordt het nagaan van<br />
consistentie van klasse-invarianten niet meer de dominanten factor. Dan verwachten we bijna<br />
gelijke metingen te krijgen zowel voor de intelligente aanpak als voor de naïeve aanpak.<br />
aanpak gemetentijd met init gemetentijd zonder init<br />
naïeve 8.6 sec 3.4 sec<br />
intelligente 5.2 sec 0.4 sec<br />
Tabel 3.7: De resultaten van de evaluatie van naïeve en de intelligente aanpak<br />
Indien we de initialisatie van de objecten en de oproep van constructoren buiten de lus doen,<br />
hebben we een factor van 7.9 gemeten. In dit geval is de naïeve aanpak tot bijna 8 keer<br />
trager dan de intelligente aanpak. Dit is te wijten aan het feit dat in JAVA de initialisatie<br />
van objecten en de oproep van constructoren veel tijd vraagt. Indien we de initialisatie buiten<br />
de lus laten dan wordt duidelijk dat de dominante factor het nagaan van voldoening van<br />
klasse-invarianten wordt en bijgevolg verkrijgen we deze factor van bijna 8.<br />
Ons voorbeeld bestaat maar uit een model uit 5 klassen en 21 invarianten. In totaal zijn er 8<br />
publieke operaties opgeroepen. We verwachten dat het toepassen van de intelligente aanpak<br />
bij grootschalige systemen toch een voordeel over de naïeve aanpak zou hebben.<br />
De code van ons experiment is terug te vinden op de CD horende bij deze thesis.
3.5. EVALUATIE 45<br />
Figuur 3.2: Klassediagram van het lopende voorbeeld.
Hoofdstuk 4<br />
Realisatie in HAT<br />
In dit hoofdstuk gaan we eerst een inleiding tot het HAT tool geven. Vervolgens gaan we<br />
beschrijven hoe de methodologie die we in hoofdstuk 3 besproken hebben in de context van<br />
het HAT tool kunnen toepassen. Namelijk welke stukken van de methodologie mogen wel<br />
geimplementeerd worden in HAT en welke niet.<br />
4.1 HAT<br />
Het HAT tool van de rma E2S (onlangs hernoemd naar ATO) is een UML tool dat Agile<br />
MDA ondersteunt. De architectuur van dit tool bestaat uit drie stukken.<br />
1. UML editor. Dat is het belangrijkste tool en biedt een grasche interface om applicatiemodellen<br />
te denieren, samenhangendheid van constraints na te gaan, transformaties<br />
van modellen naar andere modellen of naar broncode uit te voeren en documenten te<br />
genereren. Al dat wordt ondersteunt door verschillende proelen. UML proelen worden<br />
in de interactieve prole builder gedenieerd en documentproelen in de interactieve<br />
document generator.<br />
• Een UML proel wordt gebruikt om de vereiste grasche representatie van verschillende<br />
objecten te tonen of beperkingen na te gaan of een model in een ander model<br />
te transformeren en code te genereren.<br />
• Een documentproel wordt gebruikt om de vereiste documenten van een gegeven<br />
UML model te produceren<br />
2. Interactive prole builder. Een UML proel bevat OCL constraints op het model,<br />
modeldenities, modeltransformaties, codegeneratoren, specicaties van diagrammen,<br />
grasche representatie van klassen, stereotypen, ... enz.<br />
3. Interactive document generator. Deze generator maakt gebruik van documentpro-<br />
elen waarin de gebruiker de inhoud van de nodige document denieert en de volgorde<br />
beschrijft waarin de data van een UML model moet verschijnen.<br />
46
4.1. HAT 47<br />
Figuur 4.1: Architectuur van de Agile MDA Tool<br />
Figuur 4.1 geeft een overzicht van de architectuur van dit Agile MDA tool. De modeltransformatie<br />
en codegeneratie gebeurt aan de hand van de UML proelen. Deze proelen<br />
moeten eerst in de UML editor ingeladen worden. Binnen zo'n proel speciceert men een<br />
representatie van een UML model en hoe het model getransformeerd moet worden.<br />
De interactive prole editor biedt een interface om UML proelen te denieren en bewerken.<br />
Deze laat ons toe om zowel modelelementen binnen een domein te denieren als om<br />
beperkingen en codegeneratoren over dit domein te speciceren.<br />
De run-time OCL constraints die in het model zitten mogen ook geëvalueerd worden. Men<br />
moet gewoon voor elke OCL constraint op het UML model de functie GenerateCode() oproepen<br />
die drie argumenten meekrijgt. De eerste is een stringwaarde die de operatie bevat<br />
om een generator op te roepen, de tweede is het tekstbestand waarin de gegeneerde code geschreven<br />
zal worden en de derde is een stringwaarde die gebruikersgedenieerde inhoud bevat<br />
(bijvoorbeeld contextinformatie). We mogen verschillende generatoren voor dezelfde run-time<br />
OCL expressie meegeven indien dat nodig is. Deze operatie transformeert de run-time OCL<br />
expressie naar een expressieboom. De elementen in deze boom zijn OCL modelelementen<br />
die aan het OCL metamodel corresponderen. Dit metamodel denieert verschillende soorten<br />
van OCL expressiemeta-elementen: literalen, if-expressie, let-expressie, enz. Een deel van het<br />
OCL metamodel is te zien in Figuur 4.2. De meegegeven generator aan de GenerateCode()<br />
operatie gaat vervolgens schrijvers voor elk OCL modelelement oproepen. Deze schrijvers<br />
zullen eigenlijk de code voor onze applicatie produceren voor de gegeven OCL expressies.<br />
In volgende sectie zullen we de vertaling van run-time OCL expressie naar JAVA bespreken<br />
met behulp van het HAT tool van E2S.
48 HOOFDSTUK 4. REALISATIE IN HAT<br />
Figuur 4.2: Gesimpliceerde OCL meta-model<br />
4.2 Run-time OCL naar JAVA<br />
In hoofdstuk 3 hebben we een mogelijke methodologie voorgesteld om run-time OCL expressie<br />
naar JAVA te vertalen. We hebben verschillende sjablonen voor verschillende OCL modelelementen<br />
voorgesteld. Nu gaan we deze methodologie in het HAT tool van E2S toepassen.<br />
Om een run-time OCL expressie te kunnen evalueren moeten we eerst van een run-time OCL<br />
expressie een expressieboom maken. In het HAT tool gebeurt dat door de operatie GenerateCode()<br />
op te roepen. Deze expressie boom bevat OCL modelelementen. Voor elk van<br />
deze modelelementen moeten we een representatie naar een doeltaal voorzien, in ons geval<br />
is dat JAVA. Om deze representatie te kunnen produceren geven we een generator aan de<br />
GeneratorCode() operatie mee. Deze generator moet een aantal schrijvers bevatten waarin<br />
voor elk OCL modelelement een representatie naar JAVA voorzien is. De representatie van<br />
deze schrijvers bevat eigenlijk de sjablonen die we in hoofdstuk 3 voorgesteld hebben.<br />
Met andere woorden om broncode in het HAT tool te produceren moet men voor elk OCL<br />
modelelement in de expressieboom de bijbehorende schrijver met zijn bijbehorende representatie<br />
toepassen. Toch is deze oplossing niet altijd van toepassing. In subsectie 4.2.2 gaan we<br />
uitleggen wat er mis kan gaan en wat een mogelijke oplossing zou kunnen zijn.<br />
Nu gaan we voor een aantal run-time OCL modelelementen bespreken hoe we de sjablonen<br />
(voorgesteld in hoofdstuk 3) zullen toepassen.
4.2. RUN-TIME OCL NAAR JAVA 49<br />
Figuur 4.3: Abstract syntax metamodel voor ModelPropertyCallExpr<br />
4.2.1 OCL attribuut-, associatie- en operatiemodelelementen<br />
De sjablonen die we voor attributen en associaties voorgesteld hebben zijn probleemloos toepasbaar<br />
in het HAT tool. Telkens we een attribuut- of associatiemodelelement in een expressieboom<br />
ontmoeten roepen we de bijbehorende schrijvers voor deze modelelementen (resp.<br />
modelelementen van type AttributeCallExpr en modelelementen van type AssocEndExpr zie<br />
Figuur 4.3). Indien we met een operatiemodelelement te maken hebben roepen we dan de<br />
schrijver die bij dit modelelement hoort, namelijk een schrijver van type OperationCallExpr.<br />
4.2.2 OCL collectieiterator modelelementen<br />
In hoofdstuk 3 over de methodologie hebben we grondig besproken dat we voor collectieiteratoren<br />
wat extra code moeten genereren. De reden is dat deze OCL collectieoperaties geen
50 HOOFDSTUK 4. REALISATIE IN HAT<br />
tegenhangers in JAVA API hebben.<br />
Bij het doorlopen van een expressieboom gaan we voor elk OCL modelelement de bijbehorende<br />
schrijver oproepen. Deze schrijvers zullen een JAVA representatie voor het OCL modelelement<br />
produceren. Voor collectieiteratoren moeten we nieuwe hulpmethoden genereren die geen<br />
tegenhangers in JAVA hebben. Bij het doorlopen van een OCL expressieboom willen we<br />
maar een oproep naar deze extra hulpmethoden produceren. Natuurlijk moeten deze extra<br />
hulpmethoden ergens anders gegenereerd worden.<br />
Nu wordt duidelijk dat indien we met een generator te werk gaan komen we terecht in een probleem.<br />
De oproep naar een gewenste hulpmethode wordt wel geproduceerd maar de methode<br />
zelf is nergens gegenereerd.<br />
We hebben dat opgelost door voor elk OCL modelelement dat extra hulpmethoden vereist,<br />
nieuwe generatoren te creëren. Deze nieuwe generatoren zullen voor elk OCL modelelement een<br />
schrijver hebben. Natuurlijk deze schrijvers zullen een verschillende representatie (sjabloon)<br />
bevatten.<br />
Met andere woorden gaan we voor een run-time OCL expressie de operatie GenerateCode()<br />
telkens met verschillende generatoren oproepen. Bij de eerste oproep van de GenerateCode()<br />
operatie wordt een oproep naar een gewenste hulpmethode gecreëerd. Vervolgens wordt opnieuw<br />
de GenerateCode() operatie met een andere generator opgeroepen. Deze generator zal<br />
maar schrijvers voor OCL elementen van type LoopExpr hebben. Deze schrijver zal de sjablonen<br />
voor de verschillende collectieiteratoren bevatten die eigenlijk de hulpmethoden zullen<br />
produceren.<br />
4.2.3 OCL If-then-else en let modelelementen<br />
De voorgestelde sjabloon in hoofdstuk 3 voor OCL if-then-else expressie kunnen we ook<br />
in HAT implementeren. We doen dat door in de schrijvers voor OCL modelelementen van<br />
type IfExpr de sjabloon voor if-then-else expressie toe te voegen. Indien de if-then-else<br />
expressies een of meerdere collectieiteratoren bevatten dan moeten we de aanpak van subsectie<br />
4.2.2 implementeren. Namelijk roepen we een tweede keer de GenerateCode() operatie op met<br />
de generator voor collectieiteratoren.<br />
Voor OCL let-expressies gaan we de bijbehorende sjabloon in een aparte hulpmethode inkapselen<br />
zoals in hoofdstuk 3 uitgelegd was. Om dat te kunnen implementeren gaan we bij<br />
de eerste oproep van de GenerateCode() operatie een oproep naar een let methode produceren.<br />
Vervolgens gaan we een tweede keer GenerateCode() oproepen met een andere generator<br />
(generator voor OCL let expressie) die de let methode zelf gaat genereren.<br />
4.2.4 Inkapseling van collectieoperaties<br />
In subsectie 3.2.3.3 hebben we verschillende oplossingen voorgesteld voor inkapseling van OCL<br />
collectieoperatiesjablonen in JAVA methoden. Voor de transformatie van run-time OCL expressie<br />
naar JAVA in HAT hebben we de eerste oplossing geimplementeerd. We hebben dat<br />
al in subsectie 4.2.2 op de vorige pagina besproken.<br />
De tweede voorgestelde oplossing waarbij we een keer een standaard bibliotheek met alle collectieoperaties<br />
genereren, is moeilijk in de huidige versie van HAT te implementeren. Het idee
4.3. NAÏEVE AANPAK IN HAT 51<br />
van deze oplossing is dat we voor elke collectieoperatie de source (de collectie waarop we<br />
de collectieoperatie zullen toepassen) en de body ( het lichaam van de collectieoperatie in de<br />
vorm van een expressie) moeten als parameters doorgeven. Voor de source is dat geen probleem<br />
maar vermits de body meestal een uitvoerbare expressie is wordt dan de implementatie<br />
onmogelijk.<br />
De derde voorgestelde oplossing is ook niet mogelijk te implementeren in de huidige versie van<br />
HAT. De datastructuur die bijhoudt welke collectieoperatie met de bijbehorende source en<br />
body al gegenereerd was bevindt zich in het OCL metamodel horend bij een gegeven generator.<br />
We hebben al gezien dat de overgang van het UML metamodel naar het OCL metamodel<br />
gaat met de GenerateCode operatie. Maar de GenerateCode operatie wordt telkens met<br />
verschillende generatoren opgeroepen. Op deze manier is de referentie naar deze datastructuur<br />
verloren<br />
4.3 Naïeve aanpak in HAT<br />
De implementatie van de naïeve aanpak in HAT gebeurt aan de hand van een UML proel.<br />
Dit UML proel bevat eigenlijk de transformaties (vertaling van run-time OCL expressies)<br />
naar JAVA.<br />
De geproduceerde code komt overeen met de methodologie die wie in hoofdstuk 3 voorgesteld<br />
hebben. Klasse-invarianten worden in booleaanse methoden ingepakt en er is een gemeenschappelijke<br />
methode gegenereerd die een conjunctie van alle invarianten bevat.<br />
Bij operaties worden zowel pre- als postcondities gegenereerd. Deze zijn in booleaanse methoden<br />
geencapsuleerd. Vervolgens zijn deze pre- en postconditiemethoden bij de horende<br />
operatie (respectievelijk in het begin en op het einde van de operatie) toegevoegd met de<br />
assert constructie. In het begin en op het einde van het lichaam van een operatie is ook de<br />
gemeenschappelijke methode toegevoegd die alle klasse-invarianten nagaat.<br />
4.4 Intelligente aanpak in HAT<br />
Voor de implementatie van de intelligente aanpak in HAT maken we opnieuw gebruik van een<br />
UML proel dat onze transformaties bevat.<br />
Voor de vertaling van klasse-invarianten, pre- en postcondities hebben we de implementatie van<br />
de naïeve aanpak overgenomen en vervolgens hebben we een aantal aanpassingen toegevoegd<br />
om de intelligente aanpak te kunnen implementeren. Met andere woorden inkapseling van<br />
klasse-invarianten, pre- en postcondities gebeurt op precies dezelfde manier als in de naïeve<br />
aanpak: elke invariant, pre- en postconditie is in een booleaanse methode ingepakt.<br />
De aanpassing die we gemaakt hebben betreft de assertie van relevante klasse-invarianten op<br />
het einde van het uitvoeren van operaties in plaats van een conjunctie van alle aanwezige<br />
invarianten te nemen.<br />
Deze aanpassingen zijn gebaseerd op string matching.<br />
Het idee is als volgt: voor elke postconditie van een operatie zoeken we of er een object of<br />
variabele tijdens de uitvoering van de operatie aangepast is. Dat doen we aan de hand van
52 HOOFDSTUK 4. REALISATIE IN HAT<br />
Algorithm 1 Algoritme intelligente aanpak gebaseerd op string matching<br />
foreach operation<br />
if(set of postconditions not empty)<br />
foreach postcondition<br />
pat := search for pattern<br />
foreach invariant<br />
find pat<br />
if(found)<br />
print invariant using assert construction<br />
else do nothing<br />
else do nothing<br />
reguliere expressies. Vervolgens gaan we zoeken of dit object (variabele) ook in een van de<br />
klasse-invarianten betrokken is. Indien dat het geval is gaan we deze invariant afdrukken door<br />
gebruik van de assert constructie. We tonen het algoritme in Algorithm1.<br />
Dat kan in sommige gevallen goed werken maar de mogelijkheid voor het afdrukken van dezelfde<br />
invariant meerdere keren in een operatie is niet uitgesloten. Bijgevolg is onze intelligente<br />
aanpak niet meer intelligent.<br />
We willen dit probleem oplossen door in plaats van elke keer de gevonden invariant af te<br />
drukken, gaan we deze in een collectie opslaan en dan duplicaten weghalen. Vervolgens door<br />
het afdrukken van ieder element van deze collectie krijgen we het gewenste resultaat.<br />
4.5 Evaluatie<br />
Voor de evaluatie van dit tool gaan we opnieuw de voorgestelde criteria van hoofdstuk 2<br />
gebruiken. We hebben al in hoofdstuk 2 gezien dat de geëvalueerde tools geen mogelijkheden<br />
aanbieden om aanpassingen en verjningen aan de gegenereerde code uit te voeren. Wat wel<br />
mogelijk is is allen een model en de bijbehorende beperkingen te modelleren en vervolgens code<br />
te genereren. In HAT is wel mogelijk dat een gebruiker stereotypen, regels en constraints en<br />
code specicatie zelf kan denieren. Dit gebeurt met behulp van UML proelen. Eens zijn deze<br />
UML proelen op UML modellen toegepast dan verrijken deze UML proelen de modellen<br />
en bovendien controleren ze of het model met de opgestelde constraints samengaat. UML<br />
proelen kunnen ook verschillende projectproducten opleveren: documenten, testsequenties,<br />
metrieken enz.<br />
Hieronder vatten we alles samen.
4.5. EVALUATIE 53<br />
Id Criteria Opmerking Ondersteuning<br />
1 invarianten Ja<br />
2 pre-, postcondities Ja<br />
3 attributen, Ja<br />
associaties<br />
4 collecties Voor ieder collectieiterator Ja<br />
een nieuwe methode<br />
5 @pre Neen<br />
6 result Neen<br />
7 assertion Ja<br />
8 slimme controle Ja<br />
klasse invarianten<br />
Tabel 4.1: Evaluatie van HAT
Hoofdstuk 5<br />
Case study - Royal and Loyal<br />
In dit hoofdstuk willen we nagaan hoe de transformatie van OCL expressies naar JAVA die we<br />
met behulp van het HAT tool geimplementeerd hebben in werkelijkheid kunnen toepassen. Om<br />
dat te realiseren hebben we als gevalstudie het voorbeeld van [1] overgenomen. Dit voorbeeld<br />
gaat over een software systeem van een imaginaire bedrijf die Royal and Loyal (R&L) noemt.<br />
5.1 Beschrijving van de case study<br />
R&L behandelt loyaliteitsprogrammas voor bedrijven die aan hun klanten verschillende soorten<br />
van bonussen bieden. Deze toeslagen hebben meestal de vorm van extra punten of air<br />
miles maar zijn er ook andere soorten van bonussen mogelijk. Met andere woorden eender<br />
welke dienst een bedrijf wenst te bieden dan is deze in een loyaliteitsprogramma overgebracht.<br />
figuur 5.1. toont het UML klassemodel dat R&L voor het meest van zijn klanten gebruikt.<br />
De centrale klasse in het model is LoyalyProgram. Een bedrijf dat aan zijn klanten lidmaatschap<br />
in een loyaliteitsprogramma biedt noemen we ProgramPartner. Meer dan een bedrijf<br />
mag gebruik maken van hetzelfde programma. Bijgevolg kunnen klanten van alle diensten<br />
genieten die door de participerende bedrijven aangeboden zijn indien ze in een loyaliteitsprogramma<br />
participeren.<br />
Elke klant van een ProgramPartner kan in een loyaliteitsprogramma toetreden door het invullen<br />
van een formulier en het verkrijgen van een lidmaatschapskaart. De klasse Customer<br />
representeert de klanten die in het programma participeren. De lidmaatschapskaart is door<br />
de klasse CustomerCard voorgesteld. Een CustomerCard is maximaal aan een persoon geassocieerd,<br />
maar kan gebruikt worden door de ganse familie of zaak. De meeste programmas<br />
laten klanten toe om punten te sparen. Elke individuele programmapartner beslist de toekenning<br />
van punten aan zijn klanten voor een bepaalde aankoop. Elke klant kan met zijn/haar<br />
bespaarde punten specieke diensten van een van de programmapartners aankopen. Beheren<br />
van spaarpunten is in klasse LoyaltyAccount opgenomen.<br />
Op een LoyaltyAccount zijn verschillende transacties mogelijk: transacties waarbij een klant<br />
punten verkrijgt en andere waarbij een klant punten besteedt. De eerste soort is in ons systeem<br />
als de klasse Earning modelleert en de tweede als Burning. Deze twee soorten van transacties<br />
zijn als subklassen van de klasse Transaction gemodelleerd.<br />
54
5.2. VERTALING MET BEHULP VAN HAT 55<br />
Figuur 5.1: Het aangepaste model van Royal and Loyal<br />
De klasse ServiceLevel is geïntroduceerd om verschillende niveaus van diensten te besturen.<br />
Deze is gedenieerd door een loyaliteitsprogramma en is door een lidmaatschap gebruikt.<br />
5.2 Vertaling met behulp van HAT<br />
Het oorspronkelijke klasse-diagram van daarboven hebben we licht aangepast om de constraints<br />
van dit model te kunnen transformeren naar JAVA . Uit de associatieklasse Membership<br />
hebben we een gewone klasse gemaakt. De reden voor deze aanpassing was dat veel van de OCL<br />
expressies (die bij dit model horen) konden niet gevalideerd worden in HAT. In de volgende<br />
paragraaf gaan we meer concreet bespreken welke soort van constraints niet gevalideerd kon.
56 HOOFDSTUK 5. CASE STUDY - ROYAL AND LOYAL<br />
Figuur 5.2: Het model van Royal and Loyal<br />
We hebben alle constraints op het model gegenereerd met behulp van het HAT tool met<br />
paar uitzonderingen. Met sommige constraints hadden we moeilijkheden om deze te kunnen<br />
valideren in het tool zelf. Indien een beperking niet gevalideerd is dan kan deze niet gegenereerd<br />
worden. Bijgevolg moesten we de constraints licht aanpassen om deze te kunnen genereren.<br />
We gaan nu de punten bespreken waarop we aanpassingen moesten uitvoeren of gewoon niets<br />
konden doen.<br />
• Indien we in een OCL expressie een associatie hebben die een collectie van objecten<br />
voorstelt (dus multipliciteit is groter dan een) en indien deze associatie op zijn beurt<br />
een tweede associatie oproept dan worden zulke OCL expressie in HAT niet gevalideerd.
5.2. VERTALING MET BEHULP VAN HAT 57<br />
In zulke gevallen zijn er geen mogelijkheden om aanpassingen uit te voeren. Bijgevolg<br />
hebben we OCL expressies van deze soort genegeerd. We geven een voorbeeld van zo'n<br />
OCL expressie in de context LoayaltyProgram zie figuur 5.1<br />
context LoayaltyProgram<br />
inv:<br />
partners.deliveredServices -> forall(x|x.condition = true)<br />
• Indien we twee klassen hebben die met elkaar verbonden worden met een associatieklasse,<br />
dan kunnen we niet navigeren via deze associatieklasse. In OCL navigeert men in<br />
dergelijke situaties door de naam van de associatieklasse te concateneren aan de naam<br />
van het gewenste associatieeinde en deze met een punt uit elkaar te scheiden. De naam<br />
van de associatieklasse moet met een hoofdletter beginnen. In HAT worden deze OCL<br />
expressies niet gevalideerd. Dat was ook een van de redenen waarom we onze associatieklasse<br />
Membership naar een gewone klasse getransformeerd hebben. Voorbeeld van<br />
zulke expressies is de volgende OCL expressie (zie figuur 5.1)<br />
context Customer<br />
inv:<br />
Membership.account -> select(x|x.points>0)->isEmpty()<br />
• Indien we constraints in de context van een associatieklasse willen denieren dan moeten<br />
we deze in HAT denieren bij een van de betrokkene klassen bij deze associatieklasse.<br />
Met andere woorden wordt de context van deze constraint niet de associatieklasse maar<br />
een van de betrokken klassen. Daarvoor hebben we uit de associatieklasse een gewone<br />
klasse gemaakt.<br />
• Veel van de postcondities bevatten sleutelwoorden zoals @pre en result. Voor OCL expressies<br />
die het sleutelwoord result bevatten kunnen we niets doen. Deze expressies zijn<br />
niet gevalideerd. De sleutelwoorden @pre hebben we gewoon niet toegevoegd. Bijgevolg<br />
is ook de betekenis van een OCL expressie veranderd maar deze expressies zijn wel gevalideerd<br />
in HAT en gegenereerd. Indien we zulke OCL expressie negeren dan kunnen we<br />
onze intelligente aanpak niet testen. Daarom hebben we zulke expressies behouden maar<br />
we hebben deze licht aangepast aangepast . Bijvoorbeeld de volgende OCL expressie:<br />
context LoayaltyProgram::enroll(c:Customer)<br />
post:<br />
wordt<br />
participants = participants@pre->including(c)<br />
context LoayaltyProgram::enroll(c:Customer)<br />
post:<br />
participants = participants->including(c)<br />
De aanpakken die we in hoofdstuk 3 ontwikkeld hebben werkten zoals ze beschreven waren.<br />
We konden een groot deel van de code direct van ons model genereren. Indien we nu onze<br />
gegenereerde code met de handgeschreven code gaan vergelijken dan zou er toch een groot<br />
verschil zijn. Een groot deel was hierboven beschreven.<br />
We hebben een inspanning gedaan om het tool te laten evalueren op een gevalstudie. Indien<br />
we sommige aanpassingen niet gedaan hebben op ons model zouden de resultaten niet bevredigend<br />
worden. Maar de tekorten wat betreft associatieklassen kunnen altijd opgelost worden.
58 HOOFDSTUK 5. CASE STUDY - ROYAL AND LOYAL<br />
Telkens we een UML proel creëren wordt deze uit een default proel afgeleid. Deze default<br />
proel heeft een aantal ingebakken constraints (bijvoorbeeld, ). Dat zijn allemaal beperkingen<br />
die een rem kunnen worden om verschillende gevalstudies te reproduceren. Maar dat zou niet<br />
zeggen dat deze gevalstudies niet op een andere manier gemodelleerd kunnen worden in HAT<br />
om de semantiek ervan te behouden.
Hoofdstuk 6<br />
Besluit<br />
MDD zou moeten een revolutionair softwareontwikkelingstijdperk inluiden waarbij alle aandacht<br />
gevestigd wordt op abstracte modellen van een applicatie op verschillende niveaus van<br />
detail in plaats van op de code. Automatische transformaties tussen deze modellen hebben<br />
een centrale rol in MDD namelijk zorgen deze dat bepaalde aspecten correct ingevuld worden.<br />
Eens zijn deze transformaties gedenieerd kan dan code automatisch gegenereerd worden.<br />
6.1 Doelstellingen en problemen<br />
De doelstellingen waren in het begin van de thesis ruim omschreven. We wouden een bepaald<br />
aspect van de modeltransformaties bestuderen, namelijk transformaties van OCL expressies<br />
naar werkende code. De doeltaal van de transformatie moest JAVA zijn. Daarvoor hebben we<br />
twee aanpakken uitgewerkt voor deze specieke transformaties (codegeneraties). Deze twee<br />
aanpakken zijn onafhankelijk van de transformatie naar een doelprogrammeertaal. De eerste<br />
aanpak die we de naïeve noemen controleert alle constraints op ieder stabiel moment van<br />
een systeem. Deze aanpak is ook de meest intuïtieve aanpak. De tweede aanpak is meer<br />
geavanceerd. Bij deze aanpak hebben we een methode ontwikkeld die laat ons toe om op een<br />
slimme manier constraints na te gaan. Deze aanpakken zijn onafhankelijk van de doeltaal<br />
waarmee we deze transformatie (codegeneratie) zullen produceren.<br />
Oorspronkelijk hebben we intensief naar tools gezocht om deze specieke transformaties te<br />
kunnen implementeren. We wilden gebruik maken van MOFScript [23]. Al snel werd duidelijk<br />
dat we deze transformaties niet zouden kunnen implementeren. De reden was dat MOFScript<br />
niet over een OCL parser beschikte.<br />
Het schrijven van een OCL parser en dan integreren in MOFScript was ver van de doelstellingen<br />
van deze thesis. Daarom hebben we dit idee verlaten.<br />
6.2 Oplossing<br />
We hebben het HAT tool van E2S gebruikt om onze doelstellingen te kunnen implementeren.<br />
De resultaten verkrijgen van dit tool waren bevredigend voor de naïeve aanpak. De<br />
doelstellingen wat betreft deze aanpak waren bereikt.<br />
59
60 HOOFDSTUK 6. BESLUIT<br />
De oorspronkelijke mogelijkheden van HAT lieten ons niet toe om de intelligente aanpak te<br />
implementeren. Dat was eigenlijk het moment van de geboorte van de intelligente aanpak<br />
gebaseerd op string matching. Deze methode zoekt voor elke postconditie horend bij een<br />
publieke operatie welke object of variabele tijdens de uitvoering van deze operatie zou moeten<br />
aangepast worden. Vervolgens gaan we de gevonden object of variabele met een of meerdere<br />
klasse-invarianten proberen te matchen. Met deze oplossing hebben we het probleem van de<br />
intelligente aanpak niet kunnen implementeren omgezeild.<br />
Tijdens de implementatie in HAT hebben we samen met E2S naar verschillende oplossingen<br />
gezocht om wat verjningen aan het tool aan te brengen.<br />
6.3 Kritische noot<br />
Een nadeel van het HAT tool is dat er geen ondersteuning is voor sleutelwoorden zoals @pre<br />
en result. Blijkbaar is de complexiteit verhoogd om deze sleutelwoorden te implementeren<br />
dan de voordelen die dat zou opbrengen.<br />
Naast de methodologie die we voor het bereiken van de doelstellingen ontwikkeld hebben,<br />
hebben we ook een evaluatie van bestaande tools gemaakt die run-time OCL ondersteunen.<br />
Voor onze grote verassing wat wij uit de gegenereerde code gemerkt hebben is dat er geen enkel<br />
van de geëvalueerde tools een meer intelligente, slimme implementatie had voor het nagaan<br />
van voldoening van constraints. Alle tools volgen min of meer een aanpak die op onze naïeve<br />
aanpak lijkt. Dat plaats HAT tool een stap voor zijn concurrenten alhoewel nog wat werk<br />
moet gedaan worden om een een deftige implementatie van de intelligente aanpak in HAT te<br />
hebben.<br />
6.4 Toekomstig werk: uitbreidingen en aanpassingen<br />
We hebben aangegeven hoe run-time OCL expressies naar werkende code in HAT getransformeerd<br />
kunnen zijn. We sommen enkele uitbreidingen op die nuttig zijn om het systeem<br />
bruikbaarder te maken.<br />
Het zou interessant zijn om een ondersteuning voor sleutelwoorden zoals @pre en result te<br />
voorzien. Dat zou het tool bruikbaarder maken wat betreft transformaties van run-time OCL<br />
expressies.<br />
Op basis van de methodologie die we in deze thesis ontwikkeld hebben, zouden we generatoren<br />
ook in andere talen moeiteloos kunnen implementeren.<br />
Een volledige beschrijving van OCL taal kan ook beschouwd worden. De methodologie die we<br />
voorgesteld hebben, is niet volledig in de zin dat we maar een deelverzameling van de OCL<br />
taal genomen hebben. We hebben vooral op een aantal aspecten gefocust: klasse-invarianten,<br />
pre- en postcondities. Aeiding regels, initiële waarden, body-of-query operaties hebben we<br />
niet bestudeerd.<br />
Alhoewel onze aandacht in deze thesis op de JAVA programmeertaal gericht was, zou er interessant<br />
zijn om een alternatieve implementatie wat betreft aspect-georiënteerde uitbreidingen
6.4. TOEKOMSTIG WERK: UITBREIDINGEN EN AANPASSINGEN 61<br />
te beschouwen. Aspecten kunnen modularizatie van klasse-invarianten en pre- en postcondities<br />
aanbieden. Op deze manier kunnen deze klasse-invarianten en pre- en postcondities uit de<br />
code afgezonderd worden. Andere voordelen van aspect-georiënteerde programmeertechnieken<br />
moeten nog uitgebreider en dieper bestudeerd worden.
B¼lage A<br />
Lijst Van Afkortingen<br />
API Application Programming Interface<br />
DBC Design By Contract<br />
GME Generic Modeling Enviroment<br />
MDA Model Driven Architecture<br />
MDD Model Driven Development<br />
OCL Object Constraint Language<br />
OMG Object Management Group<br />
PIM Platform Independent Model<br />
PSM Platform Specic Model<br />
UML Unied Modeling Language<br />
62
Bibliograe<br />
[1] A.Kleppe, J.Warmer, The Object Constrait Language, Second Edition, Getting your models ready for<br />
MDA,Addison-Wesley, 2003<br />
[2] David S. Frankel, Model Driven Architecture: Applyoing MDA to Enterprise Computing,Joe Wikert,<br />
2003<br />
[3] A.Kleppe, J.Warmer, W.Bast,MDA explained - The model driven Architecture: practice and<br />
promices,Addison-Wesley, 2003<br />
[4] M.Fowler, Language WorkBenches and Model Driven Architecture,<br />
http://www.martinfowler.com/articles/mdaLanguageWorkbench.html<br />
[5] S.H.Czarnecki, S.Helsen, Feature-based survey of model transformation approaches,IBM SYSTEMS<br />
JOURNAL, 2006, http://www.research.ibm.com/journal/sj/453/czarnecki.pdf<br />
[6] Object Constraint Language OMG Available Specication Version 2.0, 2006,<br />
http://www.omg.org/docs/formal/06-05-01.pdf<br />
[7] J.Oldevik,MOFScript User guide (v0.6),2006, http://www.eclipse.org/gmt/mofscript/doc/MOFScript-<br />
User-Guide.pdf<br />
[8] A.Hovsepyan,S.Van Balen, B.Vanho, W.Jossen, Y.Berbers, Key Research Challenges for Successfully<br />
Applying MDD within Real -Time Embeded Software Development, 2006<br />
[9] A.Kleppe, J.Warmer, W.Bast,MDA explained - The model driven Architecture: practice and<br />
promices,Addison-Wesley, 2003<br />
[10] E2S nv, http://www.e2s.be<br />
[11] Borland Together tool, http://www.borland.com/together<br />
[12] OCL tool, http://lci.cs.ubbcluj.ro/ocle<br />
[13] Octopus tool, http://www.klasse.nl<br />
[14] Programming with assertions, http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html<br />
[15] OMG, Unied Modeling Language, http://www.uml.org<br />
[16] OMG, The Object Management Group, http://www.omg.org<br />
[17] GME, The Generic Modeling Enviroment, http://www.isis.vanderbilt.edu/projects/gme/<br />
[18] MetaEdit, Domain-Specic Modeling with MeataEdit+, http://www.metacase.com/index.html<br />
[19] Eiel, http://www.eiel.com/<br />
[20] OMG, OMG Model Driven Architecture, http://www.omg.org/mda<br />
63
64 BIBLIOGRAFIE<br />
[21] OMG, The Object Constrait Language, http://www.omg.org/docs/formal/06-05-01.pdf<br />
[22] No Magic Inc, MagicDraw - visual UML modeling and CASE tool, http://www.magicdraw.com/<br />
[23] Modelbased, MOFScript , http://www.eclipse.org/gmt/mofscript<br />
[24] E2S nv, HAT Manual, https://www.e2s.be/Content/Software_Products/Hat/Download_Data/HatManual.pdf<br />
[25] B.Meyer,Design by Contract, http://se.ethz.ch/~meyer/publications/computer/contract.pdf