Hoofdstuk 13 case: toestandsdiagrammen In dit hoofdstuk wordt het maken van de eerste versie van de toestandsdiagrammen voor het boodschappensysteem van Hans en Jacqueline uitgewerkt. 13.1 Vind klassen met dynamisch gedrag Om geen onnodig werk te doen moet eerst worden bezien voor welke klassen de toestandsdiagrammen wel en niet gemaakt zullen gaan worden. Een groot aantal klassen in deze case vertoont geen bijzonder dynamisch gedrag: Kookboek, Recept, Dag, Week, Maaltijd zullen allemaal gedurende hun levensloop maar één toestand kennen. Voor Persoon geldt hetzelfde omdat de klasse een representatie in het systeem is van gegevens die het systeem moet kennen over de werkelijke personen buiten het systeem. Hoewel de werkelijke personen waarschijnlijk zeer dynamisch gedrag vertonen zal hun afspiegeling in het systeem waarschijnlijk zeer statisch zijn. De enige klasse met relevant dynamisch gedrag is eigenlijk de klasse Voorraadkast. We gaan voor deze klasse een toestandsdiagram maken. 13.2 Maak toestandsdiagrammen 13.2.1 Vind de toestanden waarin een object zich kan bevinden De vraag die we moeten beantwoorden om eventuele toestanden van objecten in de klasse Voorraadkast te herkennen, is of het object in een specifieke toestand moet zijn om op de binnenkomende events te reageren. Een binnenkomende event als bijvoorbeeld voegtoeitem kan alleen juist verwerkt worden wanneer een bestelling zojuist gegenereerd is maar nog niet verzonden. bestellinggegenereerd is dus een toestand waarin instanties van de klasse Voorraadkast zich kunnen bevinden. Op dezelfde wijze vinden we de toestand bestellingstaatuit. Wanneer bij ontvangst van de bestelde goederen blijkt dat iets niet of in een gewijzigde hoeveelheid geleverd is, moet dit vanzelfsprekend in de voorraadadministratie worden opgenomen. Dit gebeurt doordat de gebruiker aangeeft welke ingrediënten verschillen van de bestelling. De gebruiker geeft in dat geval aan welke ingrediënten gewijzigd zijn binnengekomen. Deze event kan alleen goed verwerkt worden wanneer (1) een bestelling verstuurd is en (2) deze bestelling nog steeds bekend is bij Voorraadkast. 141
praktisch uml Een nieuwe toestand gewijzigdeontvangst is geïntroduceerd om Voorraadkast deze event op de juiste wijze te laten afhandelen. Een andere mogelijkheid is dat de gehele bestelling in juiste hoeveelheden is gearriveerd. In de toestand ontvangstbestelling worden de gegevens over de voorraad van de ingrediënten bijgewerkt. Als resultaat van deze stap zijn de volgende toestanden geïdentificeerd: bestellinggegenereerd bestellingstaatuit gewijzigdeontvangst ontvangstbestelling Wanneer we nu echter goed naar deze toestanden kijken, blijkt dat dit eigenlijk geen toestanden van de klasse Voorraadkast zijn, maar van iets als een bestelling. De namen van drie van de toestanden bevatten dit woord. Is dit dan toch geen reden om een nieuwe klasse Bestelling te introduceren? Een ander argument hiervoor is dat een bestelling altijd bij een Week hoort, maar het liefst wel door de Voorraadkast beheerd moet worden. Misschien is het introduceren van Bestelling als een associatieklasse tussen Week en Voorraadkast niet zo n slecht idee. We zullen dit idee verder uitwerken om de voor- en nadelen goed te kunnen inschatten. De klasse Bestelling bevat op het eerste gezicht een lijst met Ingrediënten, een datum en tijd waarop deze gegenereerd is, en een datum en tijd waarop deze verstuurd is. Voor een Bestelling kunnen we analoog aan bovenstaand betoog voor Voorraadkast de volgende toestanden herkennen: voorlopig: de Bestelling is gegenereerd, maar de actor kan er nog items aan toevoegen en items wijzigen. verstuurd: de Bestelling is verstuurd naar een supermarkt en mag niet meer gewijzigd worden. ontvangen: de Bestelling is ontvangen. Het enige waar we Voorraadkast nu nog voor nodig hebben is als de bestelling gewijzigd binnenkomt. Maar dat kunnen we oplossen door een kopie te maken van de lijst met Ingrediënten in de Bestelling en deze door te geven aan de Userinterface. De actor kan nu in de kopie wijzigingen aanbrengen, waarna Voorraadkast op basis van de kopie de wijzigingen in de voorraad aanbrengt. Als onze opdrachtgevers deze informatie interessant vinden, dan kan de kopie (de lijst met ontvangen Ingrediënten) ook in het Bestellingobject bewaard worden, zodat later altijd teruggezocht kan worden wanneer en bij welke supermarkt verschillen in bestelde en ontvangen goederen plaatsvonden. Wat worden nu de toestanden van Voorraadkast? Ook Voorraadkast is nu een minder dynamisch object geworden. Zelfs als we onze fantasie loslaten en de toestanden leeg en gevuld verzinnen, blijkt bij nader inzien dat er weinig tot geen verschil is in hoe Voorraadkast zijn operaties uitvoert in deze toestanden. In beide gevallen kan Voorraadkast een bestelling genereren. Als we de eis loslaten dat er een uitstaande bestelling moet zijn voordat er goederen ontvangen kunnen worden, kan ook in beide gevallen 142
ho ofdstuk 13 case: toestandsdiagrammen Voorraadkast goederen ontvangen. Een melding naar de actor dat er geen uitstaande bestellingen zijn lijkt een voldoende maatregel. Het lijkt erop dat het introduceren van een Bestelling-klasse een goed idee is. Een deel van de complexiteit van de klasse Voorraadkast wordt nu verplaatst naar Bestelling. Een dergelijke verdeel-en-heerstactiek is heel gebruikelijk bij het maken van objectgeoriënteerde systemen. Elke klasse op zich moet zo simpel mogelijk zijn. We omschrijven dat aspect wel eens met objecten zijn lui, ze laten liever het moeilijke werk aan andere objecten over. We gaan voor de klasse Bestelling een toestandsdiagram maken (figuur 13 1). Later (in 13.3) zullen we natuurlijk deze nieuwe klasse op de juiste wijze aan het klassediagram moeten toevoegen en moeten we ook de betreffende sequencediagrammen aanpassen. wijzigitem nieuw voorlopig verstuur verstuurd voegtoeitem ontvangen gooiweg ontvangen Figuur 13-1 Toestandsdiagram voor Bestelling 13.2.2 Vind voor iedere event de bijbehorende transitie(s) Een groot deel van de transities hebben we al in de vorige paragraaf herkend. De event verstuur is zo n event die alleen verwerkt kan worden in de toestand voorlopig, want de Bestelling kan natuurlijk alleen verstuurd worden als deze eerst gegenereerd is. Wanneer de event plaatsvindt komt het object in een andere toestand, de Bestelling kan namelijk niet meer aangepast worden door de actor en moet onthouden worden om later de ontvangst te kunnen controleren. De transitie die bij deze event hoort is dus de transitie van de toestand voorlopig naar de toestand verstuurd. Ook de events wijzigitem en voegtoeitem mogen alleen voorkomen als de Bestelling in de toestand voorlopig is. Het object komt daardoor niet in een andere toestand. De event ontvangen bij Bestelling mag alleen voorkomen als de Bestelling in de toestand verstuurd is. 143
praktisch uml 13.2.3 Voeg eventueel begin- en eindtoestanden toe De eindtoestand van instanties van de klasse Bestelling zal pas bereikt worden als de goederen ontvangen zijn. Alleen vanuit de toestand ontvangen mag het object uit het systeem verwijderd worden. Vanuit de begintoestand zal het object direct gaan naar de toestand voorlopig. 13.2.4 Voeg acties toe Acties zijn niet aan het toestandsdiagram toegevoegd. 13.2.5 Voeg activiteiten toe Bij de klasse Bestelling kunnen we geen activiteiten herkennen. Wel bij de klasse Voorraadkast: genereerbestelling en ontvangbestelling. Eventueel kunnen we ervoor kiezen om op basis van deze activiteiten een toestandsdiagram voor Voorraadkast te maken. De toestanden zouden dan overeenkomen met de activiteiten: genereerbestelling en ontvangbestelling. Daarnaast zou een toestand idle moeten bestaan. Een dergelijk diagram voegt naar onze mening weinig informatie toe, als we tenminste de moeite nemen om de betreffende sequencediagrammen aan te passen. Het kan dan ook achterwege blijven. 13.3 Terugkoppeling naar andere diagrammen 13.3.1 Klassediagram De nieuwe klasse Bestelling hebben we geïntroduceerd als een associatieklasse tussen Voorraadkast en Week. We stellen, om zo flexibel mogelijk te zijn, dat er meerdere Bestellingen tegelijkertijd mogen bestaan, maar slechts één per Week. Ook hebben we ontdekt dat Bestelling een optionele associatie heeft met Supermarkt. Deze associatie bestaat niet in de toestand voorlopig, maar wel in de toestanden verstuurd en ontvangen. Wat betreft de multipliciteit aan de andere kant van deze associatie geldt dat bij één supermarkt meerdere bestellingen gedaan kunnen worden. In figuur 13 2 vindt u het klassediagram aangepast aan de nieuwe situatie. Ook moet natuurlijk een definitie van Bestelling aan de modeldictionary worden toegevoegd. U vindt deze in tabel 13 1. 144
ho ofdstuk 13 case: toestandsdiagrammen Week Dag Maaltijd nummer 7 aantalmaaltijden 1.. type : MaaltijdType toonweekschema() Bestelling toestand : BestelToestand aanwezigen type Persoon kok Recept bereidingswijze bereidingstijd moeilijkheidsgraad 1.. Supermarkt faxnummer voorkeuren Voorkeur maaltijdtype Kookboek zoekrecept() << enumeration >> MaaltijdType warm koud Voorraadkast doebestelling() ontvangbestelling() vermindervoorraad() itemlist voorraad Ingredient type hoeveelheid benodigdheden << enumeration >> BestelToestand voorlopig verstuurd ontvangen Figuur 13-2 Vierde versie klassediagram voor EasyShop Tabel 13-1 Toevoeging aan modeldictionary Klasse Bestelling Omschrijving Associatieklasse tussen Voorraadkast en Week die informatie bevat over de goederen die voor die Week besteld zijn of moeten worden. 145
praktisch uml 13.3.2 Constraints Bij het aanpassen van de constraints aan de nieuwe situatie letten we natuurlijk vooral op business rules die gelden voor de nieuwe klasse. Als regels herkennen we: 1. Een bestelling heeft een associatie met een supermarkt dan en slechts dan als de bestelling niet meer voorlopig is. 2. Er mag maar één bestelling actief zijn, dat wil zeggen voorlopig of verstuurd zijn. 3. Er mag geen nieuwe bestelling gegenereerd worden als er nog een bestelling actief is. De verantwoordelijkheid voor constraint 1 leggen we bij Bestelling. 1. context Bestelling inv: if toestand = BestelToestand::voorlopig then supermarkt->isempty() else supermarkt->notempty() endif De verantwoordelijkheid voor constraints 2 en 3 leggen we bij Voorraadkast. De constraint voor regel 2 wordt dan: 2. context Voorraadkast inv: bestelling->select( toestand<>besteltoestand::ontvangen)->size() <= 1 Voor regel 3 blijkt dat de formulering een dynamische is: er mag geen nieuwe gegenereerd worden. Als we dit naar een structurele regel proberen om te zetten betekent dit dat er geen tweede actieve bestelling gemaakt mag worden, ofwel er mag maar één actieve bestelling zijn. Hieruit blijkt dat deze constraint identiek is aan regel 2. Juist door de business rules om te zetten naar een formele constraint in OCL worden we gedwongen om over de precieze betekenis na te denken. Hierboven leidt dit tot de conclusie dat twee ogenschijnlijk verschillende regels feitelijk identiek zijn. 13.3.3 Sequence- en communicatiediagrammen De interacties die aangepast moeten worden zijn boodschappenlijst versturen en binnengekomen inkopen opvoeren. In de eerste interactie gaat alles hetzelfde tot op het moment dat Voorraadkast alle benodigde Ingrediënten verzameld heeft. Op dat moment zal een Bestelling-object aangemaakt worden en telkens als Voorraadkast een benodigd Ingrediënt heeft vergeleken met de voorraad en een tekort signaleert, zal een item aan de Bestelling 146
ho ofdstuk 13 case: toestandsdiagrammen worden toegevoegd. Dit Bestelling-object zal aan de Userinterface worden doorgegeven om te worden getoond aan de actor. Toevoegingen en wijzigingen op de Bestelling worden vervolgens door de Userinterface niet aan Voorraadkast maar aan Bestelling doorgegeven. Bij het binnenkomen van inkopen zal de Userinterface aan de Voorraadkast vragen om de uitstaande Bestelling. Een kopie daarvan wordt aan de Userinterface teruggegeven en wordt vervolgens getoond aan de actor. Toevoegingen en wijzigingen zullen wederom direct worden doorgegeven aan deze kopie. De gewijzigde kopie wordt door Voorraadkast verwerkt in de Ingrediënt-objecten van de voorraad. Als laatste zal het eigenlijke Bestellingobject van Voorraadkast de boodschap ontvangen krijgen. 147