Hoofdstuk 17 case: applicatie- en implementatiemodellen In dit hoofdstuk wordt het maken van de applicatie- en implementatieversies van de diagrammen voor EasyShop, het maaltijdsysteem van en, uitgewerkt. 17.1 Start van applicatiestadium Op dit moment lijken de diagrammen voor EasyShop een redelijke mate van stabiliteit te hebben. We kunnen nu overgaan naar het volgende stadium, mits we beslissingen hebben genomen over een aantal zaken: Hergebruik In EasyShop zal geen enkele domeinklasse worden hergebruikt, aangezien dergelijk klassen niet voorhanden zijn. Wel wordt een bibliotheek gebruikt om de grafische userinterface mee te ontwikkelen. Verder ligt het niet in de bedoeling om in het kader van EasyShop herbruikbare klassen te ontwikkelen. Keuze van ontwikkelomgeving De programmeertaal waarin het systeem geschreven zal worden is Java. De ontwikkelomgeving wordt een PC met daarop een gratis beschikbare Java-editor. Gegevensbeheer We besluiten tot een eenvoudige opslagstructuur, namelijk in ASCII-files. Elke Week zal gegevens wegschrijven naar een eigen file. Elk Recept staat in een eigen file. Het Kookboek heeft een file waarin de namen van de beschikbare recepten zijn opgenomen. De Voorraadkast heeft een file waarin alle gegevens over de voorraad staan. Verder zullen alle gegevens over alle Personen in één file opgenomen worden. Dit kan omdat de verwachting is dat het aantal personen klein zal blijven. 17.2 Integreer informatie in klassediagram De eerste stap in deze volgende fase is het integreren van de informatie in alle diagrammen in het klassediagram. Met het maken van de dynamische diagrammen is een groot aan- 185
praktisch uml tal keuzes gemaakt over de klassen en hun verantwoordelijkheden. Deze keuzes worden gebruikt om het klassediagram in een nieuwe iteratieslag uit te breiden en te verbeteren. 17.2.1 Controleer klassen en associaties Afgezien van de klasse Bestelling zijn in de dynamische diagrammen van EasyShop geen nieuwe klassen geïdentificeerd. De klasse Bestelling is al toegevoegd aan het klassediagram. Toch gaan we nog een klasse toevoegen. Wanneer er namelijk een bepaald ingrediënt in voorraad is, en ook in bestelling is, en die bestelling komt binnen, dan willen we de voorraad op de juiste wijze updaten. Hiervoor moeten we weten dat het hierbij gaat om in wezen hetzelfde ingrediënt, hoewel in de voorraad en in de bestelling twee verschillende Ingrediënt-objecten bestaan. Daartoe introduceren we een klasse IngrediëntSoort, met als attributen de van het ingrediënt, het type (diepvries, vers), de eenheid waarin het verkocht wordt (liters, grammen, stuks). Elk Ingrediënt-object verwijst nu naar een IngrediëntSoort-object en daarmee hebben we dit probleem opgelost. In feite hebben we hier een verbetering van ons domeinklassemodel, door het toevoegen van een nieuwe domeinklasse. 17.2.2 Leid attributen uit de dynamische diagrammen af Bij het afleiden van attributen uit de dynamische diagrammen moeten we ons beperken tot het enige toestandsdiagram, dat van Bestelling. Bij deze klasse is een attribuut toestand nodig om de toestand waarin een Bestelling-object zich bevindt te kunnen identificeren. 17.2.3 Voeg operaties aan het klassediagram toe Zoals gewoonlijk hebben ook in deze case de dynamische diagrammen veel nieuwe operaties en extra informatie over bekende operaties, zoals parameters, opgeleverd. Volgens de regels voor koppeling van de diagrammen (uit 16.4) hebben we in hoofdstuk 11 de events uit de sequence- en communicatiediagrammen omgezet in operaties. Extra operaties die we op dit moment kunnen toevoegen zijn operaties op basis van het toestandsdiagram van Bestelling: wijzigitem voegtoeitem verstuur ontvangen Verder moeten we op dit moment beslissen hoe we de lijst met Personen die binnen het systeem bekend zijn, gaan opslaan. In 11.3.1 hebben we al aangegeven dat we dit kunnen doen door middel van een klasseattribuut en een klasseoperatie bij de klasse Persoon of door middel van een lijst in de Userinterface. We kiezen ervoor om de klasse Persoon deze verantwoordelijkheid te geven. 186
ho ofdstuk 17 case: applicatie- en implementatiemodellen Week nummer toonweekschema() 7 Dag aantalmaaltijden datum Maaltijd type : MaaltijdType heeftreceptnodig tijd : Tijdstip Bestelling toestand: BestelToestand aanwezigen Persoon allepersonen type kok Recept bereidingswijze bereidingstijd moeilijkheidsgraad wijzigitem() voegtoeitem() verstuur() ontvangen() vindpersoon() Supermarkt faxnummer voorkeuren Voorkeur maaltijdtype Kookboek zoekrecept() Voorraadkast doebestelling() ontvangbestelling() vermindervoorraad() voorraad «self» Ingredient hoeveelheid MaaltijdType warm koud benodigdheden IngrediëntSoort type eenheid «enumeration» «enumeration» BestelToestand voorlopig verstuurd ontvangen Figuur 17-1 Vijfde versie klassediagram voor EasyShop 187
praktisch uml 17.2.4 Completeer het klassediagram Figuur 17 1 bevat het klassediagram waarin de nieuwe attributen en operaties zijn opgenomen. 17.3 Voeg details toe In deze stap gaan we de klassen die tot op dit moment herkend zijn en in het klassediagram gevoegd zijn detailleren. 17.3.1 Ontwerp associaties Associaties worden in onze case allemaal in één richting gebruikt. In figuur 17 2, aan het eind van dit hoofdstuk, zijn alle associaties met hun richting opgenomen. 17.3.2 Ontwerp attributen Tijdens het ontwerp van de attributen wordt voor alle attributen bij de klassen het type bepaald. De types die in EasyShop gebruikt worden zijn over het algemeen standaardtypes zoals Integer en String. Het type van het attribuut tijd bij Maaltijd is daarop een uitzondering. We hebben een utilityklasse Tijdstip nodig. Deze moet worden toegevoegd aan het model. In figuur 6 2 staat een dergelijke klasse. Dit is typisch een klasse die uit een klassebibliotheek gehaald kan worden. 17.3.3 Ontwerp operaties Alleen voor de klasse Bestelling is een toestandsdiagram gemaakt. Uit dit diagram kunnen we pre- en postcondities voor enkele operaties van deze klasse afleiden. Preconditie bij de operatie verstuur is dat het object in de toestand voorlopig is. Postconditie bij deze operatie is naast het feit dat een fax verstuurd is natuurlijk ook dat het object in de toestand verstuurd is. Deze pre- en postcondities kunnen met behulp van OCL vastgelegd worden. context Bestelling::verstuur() pre : self.toestand = BestelToestand::voorlopig post: self.toestand = BestelToestand::verstuurd In [Warmer2003] wordt uitgebreid ingegaan op het gebruik van OCL voor pre- en postcondities. Het vaststellen van de condities voor de overige operaties laten we als oefening aan de lezer over. 188
ho ofdstuk 17 case: applicatie- en implementatiemodellen 17.3.4 Overerving Met de huidige informatie hoeven de overervingsrelaties niet aangepast te worden. 17.3.5 OCL-constraints In deze stap beslissen we hoe we de constraints die in hoofdstuk 7 voor EasyShop zijn aangegeven, zullen implementeren. We hebben de volgende constraints geïdentificeerd: 1. context Voorraadkast inv: Voorraadkast.allInstances()->size() <= 1 2. context Kookboek inv: Kookboek.allInstances()->size() <= 1 3. context Maaltijd inv: aanwezigen->includes( kok ) 4. context Maaltijd inv: kok->notempty() implies recept->notempty() and recept->notempty() implies kok->notempty() 5. context Dag inv: Dag::aantalMaaltijden = maaltypen->size() 6. context Persoon inv: voorkeur->size() = Dag::aantalMaaltijden 7. context Persoon inv: voorkeur.maaltijdtype = voorkeur.maaltijdtype->asset() 8. context Bestelling inv: if toestand = BestelToestand::voorlopig then supermarkt->isempty() else supermarkt->notempty() endif 9. context Voorraadkast inv: bestelling->select( toestand <> BestelToestand::ontvangen)->size() <=1 189
praktisch uml De eerste twee constraints implementeren we met behulp van het singleton pattern, zie hiervoor 18.4.2. De derde constraint implementeren we door een controle in te bouwen in de operatie zetkok(p: Persoon) van Maaltijd. De vierde constraint implementeren we door eenzelfde soort controle in de operatie zetkok op het bekend zijn van een Recept. Dit betekent dat we altijd eerst een recept bekend moeten maken en daarna de kok. De vijfde constraint implementeren we door bij de creatie van een Dag-object tegelijkertijd alle benodigde Maaltijd-objecten te creëren. Vervolgens staan we het afzonderlijk weggooien van Maaltijd-objecten (dus zonder het bijbehorende Dag-object weg te gooien) niet toe. In het klassediagram geven we deze lifetime dependency (zie 4.4.8) aan door de associatie tussen Dag en Maaltijd te veranderen in een compositieassociatie. De laatste twee constraints implementeren we als een controle in het creëren door een Persoonobject van Voorkeur-objecten. 17.3.6 Packages Het systeem is nog steeds zo klein dat packages niet nodig zijn. In 17.4.3 zullen we hierop terugkomen. 17.4 Voeg nieuwe klassen toe Het toevoegen van nieuwe klassen is verdeeld in het toevoegen van applicatieklassen (in dit geval de complete userinterface) en het toevoegen van implementatieklassen (in dit geval de opslagstructuur). 17.4.1 Applicatieklassen of het definiëren van de userinterface De volgende stap in de werkwijze heeft betrekking op het definiëren van de userinterface van EasyShop. Identificeer views Voor het identificeren van de views worden de interacties uit hoofdstuk 11 als uitgangspunt genomen. We zoeken naar informatie die getoond moet worden door de userinterface. Het blijkt dat een lijst met Personen van het type gast wordt getoond en informatie uit de volgende domeinklassen: Week Dag Maaltijd Kookboek Recept Bestelling 190
ho ofdstuk 17 case: applicatie- en implementatiemodellen Voorraadkast Supermarkt Voor elk van deze klassen en voor de klasse Persoon introduceren we een applicatieklasse. De van een dergelijke applicatieklasse is de van de domeinklasse met het woord View eraan toegevoegd, bijvoorbeeld WeekView. Elk van deze applicatieklassen heeft een associatie met de domeinklasse waarvan informatie getoond moet worden. Definieer controllers Bij het definiëren van controllers gaan we terug naar de use-cases, ervan uitgaande dat de use-cases gestructureerd zijn naar de taken die de gebruiker met het systeem moet kunnen uitvoeren. We gaan alle use-cases af om de juiste controllers te vinden. Inloggen De controle tijdens het inloggen ligt in het main programma. We introduceren geen extra controller voor deze taak. Agenda invullen en Gast(en) aangeven Tijdens het invullen van de agenda wordt de nieuwe klasse WeekView gebruikt. We introduceren voor het afhandelen van de algemene gebruikersinteracties een klasse WeekController en een klasse MaaltijdController voor het afhandelen van het aangeven van de aanwezigheid bij een enkele Maaltijd. Recept kiezen en Kookboek inzien Bij de taken in use-cases Recept kiezen en Kookboek inzien ligt de controle bij een nieuwe klasse KookboekController. Boodschappenlijst versturen en Binnengekomen inkopen opvoeren In beide use-cases moet een Bestelling getoond en veranderd kunnen worden. Naast de BestellingView wordt een nieuwe klasse BestellingController geïntroduceerd. Voorraad inzien In deze use-case moet de voorraad getoond worden. Hiertoe introduceren we een klasse VoorraadKastController. 17.4.2 Implementatieklassen Het vinden van utilityklassen is relatief simpel. In EasyShop is datum een belangrijk type van attributen. Dit is een utilityklasse. We zullen proberen deze klasse te vinden in een bibliotheek. Is er geen geschikte bibliotheek dan zullen we de klasse zelf implementeren. Verder is het handig om een mechanisme te hebben om pre- en postcondities van operaties te testen. Dit mechanisme brengen we onder in een utilityklasse Assertie. Vervolgens is de aandacht gericht op de opslagmechanismen. Het inlezen en wegschrijven van alle files wordt op een standaardmanier geregeld door twee klassen te introduceren: Schrijver en Lezer. De klasse Schrijver bevat alle operaties die naar file schrijven 191
praktisch uml zoals schrijfrecept, schrijfkookboek, enz. De klasse Lezer bevat alle leesoperaties zoals leesrecept, leeskookboek, enz. WeekController MaaltijdController WeekView 7 DagView MaaltijdView Week Bestelling BestellingView isingevuld 7 Dag Persoon aanwezigbij kok Maaltijd ReceptView Recept BestellingController Supermarkt Voorraadkast Voorkeur benodigdheden Ingrediënt voorraad IngrediëntSoort Kookboek KookboekView KookboekController VoorraadkastView VoorraadkastController Figuur 17-2 Zesde versie klassediagram voor EasyShop 192
ho ofdstuk 17 case: applicatie- en implementatiemodellen 17.4.3 Overige stappen Het meest interessante van de overige stappen is het vastleggen van de structuur in de userinterface-klassen. De applicatieklasse WeekView wordt een apart window in onze grafische userinterface en zal dan ook overerven van de juiste klasse uit onze GUI-bibliotheek. DagView en MaaltijdView worden onderdelen van WeekView en zullen dan ook overerven van een andere klasse uit onze GUI-bibliotheek. WeekView heeft een associatie met de Week die hij laat zien. Ook is een associatie met het Persoon-object waarvan de aanwezigheid getoond moet worden, nodig. In figuur 17 2 staat het klassediagram waarin de userinterface-klassen zijn weergegeven. Ter illustratie wordt in figuur 17 3 getoond hoe het weekschema van er op het scherm uit zou kunnen zien. Maandag Dinsdag Woensd. Donderd. Vrijdag Zaterdag Zondag Ontbijt Lunch Diner (kok) Jos Anneke (kok) Figuur 17-3 Een voorbeeld-weekview Op dezelfde wijze zal de klasse KookboekView een apart window zijn waar ReceptView een onderdeel van is. Het invullen van alle andere details wordt als oefening aan de lezer overgelaten. Op dit moment is het systeem zo groot geworden dat het zinvol is het op te delen in packages. We gebruiken hiervoor een three-tier-architectuur en introduceren de volgende packages: Userinterface: bevat alle userinterface-klassen. Domein: bevat alle domeinklassen. 193
praktisch uml Opslag: bevat de Schrijver- en Lezer-klassen. Tools: bevat alle utilityklassen. In figuur 17 4 vindt u een diagram waarin alleen de packages zijn opgenomen. EasyShop DomeinPackage ToolsPackage UserInterfacePackage OpslagPackage Figuur 17-4 Het packagediagram voor EasyShop 17.5 Maak dynamische diagrammen Het maken van de extra toestandsdiagrammen en de aanpassingen in de sequencediagrammen is niet bijzonder ingewikkeld. Wij laten dit dan ook als oefening aan de lezer. 17.6 Integreer alle informatie Het maken van het laatste model voor we gaan implementeren is nu niets anders meer dan het samenvoegen van de informatie die we op dit moment tot onze beschikking hebben. Op dit moment moet van alle klassen een complete beschrijving gemaakt kunnen worden. Als voorbeeld is de klasse Maaltijd uitgewerkt in tabel 17 1. 194
ho ofdstuk 17 case: applicatie- en implementatiemodellen Tabel 17-1 Klassebeschrijving van de klasse Maaltijd Klasse: Maaltijd Attributen: : String type: {warm, koud} heeftreceptnodig: Boolean tijd: Tijdstip Operaties: geefaanwezigen(): lijst Persoon zetrecept(r: Recept): void aanwezig(p: Persoon): void geefingrediënten(): lijst Ingrediënt Associatie-einden: kok: Persoon isaanwezigbij: lijst Persoon recept: Recept 17.7 Vervolg van de case Het maaltijdsysteem van en is zo kleinschalig dat het maken van component- en deploymentdiagrammen niet zinvol is. Deze diagrammen worden in hoofdstuk 14 behandeld. Het uitwerken van de rest van de case laten we vanaf dit moment dan ook aan de lezer over. Op de website van Klasse Objecten http://www.klasse.nl is een in Java uitgewerkte versie van de case te vinden. 195