Opdracht 4: Modelleren van werknemers.
© Harry Broeders.
Deze opdracht is bedoeld als een introductie in modelleren in UML met behulp
van Visual Paradigm. Visual Paradigm for UML standard Edition 10.0 is
beschikbaar in lokaal D1.052 en in het studielandschap van Elektrotechniek.
Bij het maken van deze opdrachtbeschrijving is gebruik gemaakt van Visual
Paradigm for UML standard Edition 10.0. Versie 11.0 is sinds 16 december 2013
beschikbaar, maar wij gebruiken deze versie op het practicum nog niet. De
license voor Visual Paradigm for UML standard Edition 10.0 van de HHS mag je
ook legaal thuis gebruiken maar uiteraard alleen voor
studiedoeleinden. Je kunt de software en de license vinden op de
BlackBoard course van OGOPRG.
Als je op je eigen laptop werkt, dan kun je ook Visual Paradigm for UML
Standard Edition 11.0 gebruiken. Deze versie werkt niet met een licentiefile
maar met een key. De key voor de HHS te vinden op de BlackBoard course van
OGOPRG. Je mag deze key ook legaal thuis gebruiken maar uiteraard alleen voor
studiedoeleinden. Pas wel op: projecten uit versie 11.0 kun je
niet openen in 10.0.
Als je echt alles wil weten dan kun je de Visual
Paradigm for UML user's guide
raadplegen.
Een simpel model van een salarisadministratie van een bedrijf.
We beginnen met het maken van een eenvoudig class diagram. In dit class
diagram gaan we de classes Bedrijf
en Werknemer
modelleren met hun onderlinge relatie.
- Start Visual Paradigm for UML 10.0 via het start menu.
- Kies de menuoptie File, New Project
- Selecteer C++ als Language en vul een Project
name in. Klik vervolgens op Create Blank
Project:

-
- Kies de menuoptie File, New Diagram, UML
Diagrams, Class Diagram
- Klik op het Class knopje.

-
- Klik vervolgens op het tekenveld en type de naam van de class Bedrijf in.

-
- Voeg ook de class Werknemer toe aan het
diagram.
- Klik op het Association knopje.

-
- Klik vervolgens op de class Bedrijf en houd de
linker muisknop ingedrukt, beweeg de muis naar Werknemer en laat daar de muisknop weer los. Label de
associatie heeft in dienst om aan te geven dat
een bedrijf werknemers in dienst heeft.

-
- Als je de associatie selecteert (links klikken) en vervolgens aan de
rechterkant van de associatie rechts klikt dan kun je bij Multiplicity 0..* kiezen.
- Selecteer vervolgens bij Assiciation End To, Multiplicity 0..*:

--
- We hebben nu aangegeven dat een Bedrijf
meerdere Werknemers in dienst kan hebben.

-
- Klik rechts op de rechterkant van de associatie en kies het
pop-up menu Edit Role Name...

-
- Vul vervolgens de rol naam werkers in.

-
- Om er voor te zorgen dat hiervoor ook de juiste code wordt
gegenereerd moeten we de volgende stap uitvoeren: Klik rechts op
de rechterkant van de associatie en kies het pop-up menu Visibility, private.

-
- Om er voor te zorgen dat hiervoor ook de juiste code wordt
gegenereerd moeten we de volgende stap uitvoeren: Klik rechts op
de linkerkant van de associatie en kies het pop-up menu Navigable, False.

-
- De associatie ziet er nu als volgt uit:

-
- We gaan nu attributes (membervariabelen) en operations (memberfuncties)
aan de classes toevoegen. Klik rechts op de class Werknemer en kies de menuoptie Add, Attribute.

-
- Je kunt nu de naam en het tpe van de attribute invullen: naam: string. Druk vervolgens op Enter zodat je het volgende attrubute kunt invoeren.
- Geef de class Werknemer ook een attribute
salaris van het type double. Druk (nadat je op Enter hebt gedrukt) op Esc
om het invoeren van attributen te stoppen.
- Voeg operaties toe aan de class Werknemer om
het salaris en de naam op te vragen: geefSalaris
en geefNaam. Vergeet niet om het returntype op
te geven.

-
- Vergeet niet om aan te geven dat deze operaties const zijn (ze zullen de receiver niet wijzigen).
Dit kun je doen door rechts te klikken op de memberfunctie en te kiezen
voor de menuoptie Code Detail, const te kiezen.

-
- Als je ook in het UML klassediagram wil zien dat deze memberfuncties
const zijn, dan kun je dat doen door niets te selecteren en rechts
te klikken in het diagram. Kies vervolgens voor: Presentation Options, Operation
Display Options, Show Code Details.

-
Ik heb deze optie voor het vervolg van deze handleiding weer uitgezet.
- Klik rechts op de class Werknemer en kies de
menuoptie Add, Constructor.

-
- Klik nu rechts op de constructor en kies voor de menuoptie Open Specification...

-
- Je kunt nu in het tabblad Parameters de
parameters van de constructor toevoegen door op het Add... knopje te klikken.

-
- Geef de constructor een parameter genaamd n
van het type string en een parameter s van het type double.

-
- Voeg nu zelf bij de class Bedrijf de operatie
neemInDienst toe waarmee een Werknemer in dienst kan worden genomen. Bij het
toevoegen van de parameters van deze operatie kun je een zelf gedefinieerd
type (class) selecteren. Klik op Werknemer.
-
- Als we deze parameter als een reference
willen meegeven (om een kopietje te voorkomen) dan moeten we dat in het
Type modifier veld
wijzigen in &.

-
- Als deze parameter const moet zijn kun je
aanvinken in het Parameter Code Details tabblad:
Let op! In dit geval moet dat
niet! Want een Bedrijf moet een
Werknemer ook kunnen beïnvloeden).

-
- Voeg bij de class Bedrijf de memberfunctie
printSalarisOverzicht toe. Niet vergeten: printSalarisOverzicht is een const memberfunctie.
-

-
- We gaan nu in een sequence diagram aangeven wat er moet gebeuren als de
boodschap printSalarisOverzicht naar een object
van de class Bedrijf wordt gestuurd. Klik rechts
op de operation printSalarisOverzicht en kies de menuoptie Referenced Diagrams, UML
Diagrams, Sequence Diagram, Create Sequence Diagram.

-
- Maak een actor Boekhouder en een life line
voor het object HHS van de class Bedrijf.

-
- -Beweeg de muis over de rechthoek onder de
actor Boekhouder en klik op het knopje Message ->
LifeLine.

-
- Kies de lifeline HHS : Bedrijf en kies vervolgens de operation printSalarisOverzicht.

-
- Voeg zelf een object werker van de class Werknemer aan het sequence diagram toe. Verstuur nu
vanuit de message printSalarisOverzicht de
messages geefNaam en geefSalaris naar de Werknemer werker.

-
- We gaan nu aangeven dat het versturen van de messages geefNaam en geefSalaris
herhaald moet worden voor alle Werknemers die
het Bedrijf HHS in
dienst heeft. Klik links op de message geefNaam,
hou de Ctrl toets ingedrukt en klik links op de
message geefSalaris. Klik nu rechts op de muis
en kies de menuoptie Create Combined Fragment,
loop.

-
- Als we de loop een stukje naar onderen
schuiven onstaat het volgende plaatje:

-
- De constraint (voorwaarde) van de loop kunnen
we invullen door rechts op de loop te klikken de de menuoptie Operand, Edit Operand,
Operand (no constaint) te kiezen.

-
- Vul in bij Constraint: werker in werkers. Klik
vervolgens op de knop OK.

-
- Na enig schuiven krijg je het onderstaande plaatje. Volgens de UML
standaard moet er een * voor de constraint staan maar dat is mij met Visual
Paradigm niet gelukt:

-
- Je kunt het sequence diagram ook eenvoudig omzetten naar een
communications diagram. Klik rechts op het sequence diagram en kies de
menuoptie Synchronise to Communication
Diagram.

-
- Klik op de knop Code en kies de menuoptie Instant
Generator, C++...

-
- Vink salarisadmin aan, kies een geschikt Output path en klik op
de knop Advanced
Options...

-
- Zet alle opties zoals hieronder aangegeven (de oranje pijlen wijzen de
opties die gewijzigd zijn aan):

-
- Klik op OK en klik vervolgens op Generate.
- Start Microsoft C++ via het start menu. Maak een nieuwe console
applicatie. Kies File, New, Project... kies vervolgens Win32 Console Application en geef het project een naam
salarisadmin, klik op OK en daarna op Next. Vink
Empty project aan en druk op Finish. Voeg de door Visual Paradigm gegenereerde
files Werknemer.cpp en Bedrijf.cpp aan het project toe, via de knop Add
Existing Item of type Shift+Alt+A. Het is niet
de bedoeling dat je deze files verplaatst (Visual Paradigm moet ze ook nog
kunnen vinden). Onthoud het pad waar de files Werknemer.cpp en Bedrijf.cpp staan. Klik op de knop Add New Item of
type Ctrl+Shift+A, kies C++ file, type bij Name:
main.cpp en type bij Location: het pad waar de
files Werknemer.cpp en Bedrijf.cpp staan. Bekijk de structuur van het project
in de Solution Explorer.

-
- Open nu de files Bedrijf.cpp en Werknemer.cpp door er dubbel op te klikken in de
Solution Explorer. Open ook de header files Werknemer.h en Bedrijf.h
door rechts op de naam te klikken en de menuoptie Open
Document te kiezen.

-
- Je ziet dat Visual Paradigm alleen het class diagram heeft omgezet naar
C++ code. Het gedrag dat gemodelleerd is in het sequence diagram zullen we
dus zelf moeten omzetten naar C++ code. Als het goed is heeft Visual
Paradigm (ongeveer) de volgende code gegenereerd:
- Je ziet dat er nog wel het een en ander aangepast kan worden: Alle (nog
niet ingevulde) functies "gooien" een exception met behulp van throw "Not yet
implemented". Dit geeft bij het uitvoeren een foutmelding, zie
eventueel paragraaf 6.8 van het dictaat. Deze throw instructies moeten vervangen worden
door de juiste code (zie hieronder). #include <exeption> kan dan verwijderd
worden.
- Vul de volgende testcode in main.cpp in:
#include <iostream>
using namespace std;
#include "Werknemer.h"
#include "Bedrijf.h"
int main() {
Werknemer harry("Harry Broeders", 4023.23);
Werknemer john("John Visser", 4078.34);
Bedrijf hhs;
hhs.neemInDienst(harry);
hhs.neemInDienst(john);
hhs.printSalarisOverzicht();
cin.get();
return 0;
}
- Implementeer de memberfuncties in Bedrijf.cpp
als volgt (printSalarisOverzicht moet worden
geïmplementeerd zoals in het sequence diagram is getekend):
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#include "Bedrijf.h"
void Bedrijf::neemInDienst(Werknemer& w) {
werkers.push_back(&w);
}
void Bedrijf::printSalarisOverzicht() const {
for (auto w: werkers) {
cout << w->geefNaam() << " : " << w->geefSalaris() << endl;
}
}
- Implementeer de memberfuncties in Werknemer.cpp als volgt:
#include <string>
using namespace std;
#include "Werknemer.h"
string Werknemer::geefNaam() const {
return naam;
}
double Werknemer::geefSalaris() const {
return salaris;
}
Werknemer::Werknemer(string n, double s): naam(n), salaris(s) {
}
- Compileer en run het programma en controleer de uitvoer:

-
Een uitgebreider model van een salarisadministratie van een bedrijf.
We gaan het model en het programma nu verder uitbreiden. Let er als je
"overschakeld" van Microsoft Visual C++ naar Visual Paradigm goed op dat je
eerst alle bestanden opslaat. Dit kan met de sneltoets Ctrl+Shift+s. Als je daarna overschakeld naar Visual
Paradigm kun je de diagrammen bijwerken door op de knop Code te klikken en te kiezen voor de menuoptie C++ Round-trip, Reverse Code...
Let op: Reverse source on
demand moet je uitvinken!
We bedenken dat er verschillende soorten werknemers kunnen zijn:
- Freelancers.
Een FreeLancer
verdient een vast bedrag per gewerkt uur.
- Vaste werknemers.
Een VasteKracht
verdient een vast maandsalaris.
Opdracht 4a.
Maak in Visual Paradigm de class Werknemer
abstract door de memberfunctie geefSalaris abstract (pure
virtual) te maken. Dit kun je eenvoudig doen door rechts te klikken op
de operation in het class diagram en de menuoptie Model Element Properties, Abstract te kiezen. Je moet nog wel zelf aangeven
dat de class Werknemer nu abstract geworden is door rechts
te klikken op de class en de menuoptie Model
Element Properties, Abstract te
kiezen. De constructor van Werknemer heeft nu nog maar 1
parameter (van het type string ) nodig. De private
variabele salaris moet worden verwijderd. In de abstract
class Werknemer moet je een virtuele destructor
definiëren. Weet je nog waarom?
Breid het class diagram nu uit met 2 classes
Freelancer en VasteKracht die beide overerven
van Werknemer .
Geef de class VasteKracht :
- een constructor waarin de naam en het maandsalaris worden
doorgegeven.
- een private variabele
maandSalaris waarin het
maandsalaris wordt opgeslagen.
- een overridden memberfunctie
geefSalaris . Deze
functie moet het maandsalaris teruggeven. Vergeet niet om deze
memberfunctie const te maken (zie stap 22).
Geef de class Freelancer :
- een constructor waarin de naam en het uurloon worden doorgegeven.
- een private variabele
uurLoon waarin het uurloon
wordt opgeslagen.
- een memberfunctie
declareerUren waarmee het aantal
gewerkte uren (een geheel getal) gedeclareerd kan worden.
- een private variabele
gewerkteUren waarin het aantal
gewerkte uren (een geheel getal) wordt opgeslagen.
- een overridden memberfunctie
geefSalaris . Deze
functie moet het maandsalaris als volgt berekenen: salaris =
uurloon * aantal gewerkte uren. Vergeet niet om deze memberfunctie
const te maken (zie stap 22).
Als je het goed doet ziet het class diagram er ongeveer
als volgt uit:
Genereer de C++ code opnieuw, zie stap
42. Let op! Met behulp
van het tabblad ModelElements kun je er voor
zorgen dat de code van Bedrijf.cpp en Bedrijf.h niet overschreven wordt:
Schakel over naar Microsoft Visual C++ en voeg de
bestanden VasteKracht.cpp en Freelancer.cpp toe aan het project.
Vul de code van VasteKracht en
FreeLancer waar nodig aan en pas de code van
Werknemer aan.
Vul de volgende testcode in main.cpp in:
#include <iostream>
using namespace std;
#include "Bedrijf.h"
#include "VasteKracht.h"
#include "Freelancer.h"
int main() {
Bedrijf hhs;
VasteKracht harry("Harry Broeders", 4023.23);
hhs.neemInDienst(harry);
VasteKracht john("John Visser", 4078.34);
hhs.neemInDienst(john);
Freelancer beun("Beun de Haas", 60.00);
hhs.neemInDienst(beun);
beun.declareerUren(120);
hhs.printSalarisOverzicht();
cin.get();
return 0;
}
De juiste uitvoer is:

|
We gaan het model en het programma nu nog verder uitbreiden. Vergeet niet om
eerst de aangepaste code weer in te lezen in Visual Paradigm door op de knop
Code te klikken en te kiezen voor de menuoptie C++ Round-trip, Reverse Code...
We voegen een nieuwe soort werknemers toe:
- Managers.
Een Manager
is een werknemer die aan minstens 1 andere
werknemer leiding geeft. De meeste managers hebben zelf ook weer een
manager boven zich. Het bedrijf dat we als voorbeeld nemen heeft een
orginele manier bedacht om het salaris van een manager te bepalen. Het
salaris van de manager is 1.5 maal zo hoog als het gemiddelde salaris van
de mensen waaraan deze manager direct leiding geeft.
Opdracht 4b.
Breid het class diagram nu uit met de class
Manager die overerft van Werknemer .
Geef de class Manager :
- een constructor waarin de naam wordt doorgegeven.
- een associatie met
Werknemer om aan te geven dat een
Manager aan 1 of meer Werknemer s leiding
geeft.
- een memberfunctie
geefLeidingAan waarmee een
Werknemer ondergeschikt aan een Manager
gemaakt kan worden.
- een overridden memberfunctie
geefSalaris . Deze
functie moet het maandsalaris berekenen volgens de hierboven
gegeven formule. Vergeet niet om deze memberfunctie
const te maken (zie stap 19).
Als je het goed doet ziet het class diagram er ongeveer
als volgt uit:
Tip: De pijl die de leesrichting van de associatie
weergeeft kun je zichtbaar maken door rechts te klikken op de
associatie en te kiezen voor: Presentation
Options, Show Direction.
Genereer de C++ code voor Manager , zie stap 42. Schakel over naar Microsoft Visual C++ en
voeg het bestand Manager.cpp toe aan het
project.
Vul de code van Manager waar nodig aan.
Vul de volgende testcode in main.cpp in:
#include <iostream>
using namespace std;
#include "Bedrijf.h"
#include "VasteKracht.h"
#include "Freelancer.h"
#include "Manager.h"
int main() {
Bedrijf hhs;
VasteKracht harry("Harry Broeders", 4023.23);
hhs.neemInDienst(harry);
VasteKracht john("John Visser", 4078.34);
hhs.neemInDienst(john);
Freelancer beun("Beun de Haas", 60.00);
hhs.neemInDienst(beun);
Manager hidde("Hidde de OpleidingsManager");
hhs.neemInDienst(hidde);
hidde.geefLeidingAan(harry);
hidde.geefLeidingAan(john);
Manager sander("Sander de Directeur");
hhs.neemInDienst(sander);
sander.geefLeidingAan(hidde);
sander.geefLeidingAan(beun);
beun.declareerUren(120);
hhs.printSalarisOverzicht();
cin.get();
return 0;
}
De juiste uitvoer is:

|
We gaan het model en het programma nu voor de laatste keer uitbreiden.
We voegen een nieuwe soort werknemers toe:
- ManagerAssistenten.
Modeleer de benodigde class ManagerAssistent
als gegeven is
dat elke Manager
hoogstens één ManagerAssistent
heeft. Elke ManagerAssistent
assisteert altijd slechts één
Manager
. Bij het berekenen van het salaris van de
Manager
wordt het salaris van de ManagerAssistent
niet meegenomen. Het salaris van de ManagerAssistent
is 45%
van het salaris van zijn/haar Manager
.
Opdracht 4c.
Breid het class diagram nu uit met de class
ManagerAssistent die overerft van Werknemer .
Geef de class ManagerAssistent :
- een constructor waarin de naam (van de
ManagerAssistent ) en een Manager wordt
doorgegeven.
- een associatie om aan te geven dat een
ManagerAssistent 1 Manager assisteert.
- een memberfunctie
assisteert waarmee een
ManagerAssistent "gekoppeld" kan worden aan een andere
Manager .
- een overridden memberfunctie
geefSalaris . Deze
functie moet het maandsalaris berekenen volgens de hierboven
gegeven formule.
Genereer de C++ code opnieuw. Schakel over naar
Microsoft Visual C++ en voeg het bestand ManagerAssistent.cpp toe aan het project.
Vul de code van ManagerAssistent waar nodig
aan.
Vul de volgende testcode in main.cpp in:
#include <iostream>
using namespace std;
#include "Bedrijf.h"
#include "VasteKracht.h"
#include "Freelancer.h"
#include "Manager.h"
#include "ManagerAssistent.h"
int main() {
Bedrijf hhs;
VasteKracht harry("Harry Broeders", 4023.23);
hhs.neemInDienst(harry);
VasteKracht john("John Visser", 4078.34);
hhs.neemInDienst(john);
Freelancer beun("Beun de Haas", 30.00);
hhs.neemInDienst(beun);
Manager hidde("Hidde de OpleidingsManager");
hhs.neemInDienst(hidde);
hidde.geefLeidingAan(harry);
hidde.geefLeidingAan(john);
Manager sander("Sander de Directeur");
hhs.neemInDienst(sander);
sander.geefLeidingAan(hidde);
sander.geefLeidingAan(beun);
ManagerAssistent annelies("Annelies de Assistent", hidde);
hhs.neemInDienst(annelies);
beun.declareerUren(120);
hhs.printSalarisOverzicht();
cout<<"Annelies maakt promotie!"<<endl;
annelies.assisteert(sander);
Freelancer manus("Manus van Alles", 34.00);
hhs.neemInDienst(manus);
sander.geefLeidingAan(manus);
manus.declareerUren(100);
hhs.printSalarisOverzicht();
cin.get();
return 0;
}
De juiste uitvoer is:

|
Ga eventueel verder met opgave 5 (niet verplicht)...