Opdracht 4: Modelleren van werknemers.
© Harry Broeders.
Deze opdracht is bedoeld als een introductie in modelleren in UML met behulp
van Together. Together is alleen beschikbaar in lokaal D1.052 en in het
studielandschap van Elektrotechniek. Je kunt eventueel de demo versie van
Borland Together 2008 gebruiken:
http://www.borland.com/downloads/download_together.html.
Bij het maken van deze opdrachtbeschrijving is gebruik gemaakt van Borland
Together 6.1. Wij gebruiken nu versie 6.2. De icoontjes zijn wat meer
gekleurd maar verder zijn er geen noemenswaardige verschillen. Als je echt
alles wil weten dan kun je de
userguide
van Borland Together 6.2
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 Together via het start menu.
-
Kies de menuoptie File,
New Project...
-
Vul de Project name in, kies een
Location en selecteer
C++ als Default
language.

-
-
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 Aggregation knopje.

-
-
Klik vervolgens op de class Bedrijf en daarna
op Werknemer om een aan te geven dat een bedrijf
werknemers in dienst heeft.

-
-
Als je de aggregatie selecteert (links klikken) en vervolgens rechts klikt
op de aggregatie kun je het popup menu
Properties... kiezen.

-
-
Je kunt nu in de Inspector bij het veld
label de naam van de aggregratie invullen:
heeft in dienst

-
-
We willen nu aangeven dat een Bedrijf meerdere
Werknemers in dienst kan hebben. Om er voor te
zorgen dat hiervoor ook de juiste code wordt gegenereerd moet dit nogal
omslachtig. Selecteer de aggregatie, klik rechts en kies het popup menu
Choose Pattern...

-
-
Dubbelklik op het mapje link. Selecteer
Aggregation 0..n as vector of pointers en vul
als Name in:
werkers.

-
-
We gaan nu attributes (membervariabelen) en operations (memberfuncties) aan
de classes toevoegen. Selecteer de class Werknemer
(links klikken), klik rechts en kies de menu optie
New, Attribute.

-
-
Je kunt nu in de Inspector bij het veld
name de naam van de attribute invullen:
naam. In het veld type
kun je het type van de attribute invullen:
string.

-
-
Geef de class Werknemer ook een attribute
salaris van het type
double.
-
Voeg operaties toe aan de class Werknemer om het
salaris en de naam op te vragen: geefSalaris en
geefNaam. Vergeet niet om aan te vinken dat deze
operaties const zijn (ze zullen de receiver niet
wijzigen).

-
-
Selecteer de class Werknemer (links klikken),
klik rechts en kies de menu optie New,
Constructor. Je kunt nu in de
Inspector bij het veld
parameters op een knopje klikken (rechts naast
het invoerveld) om een dialog te openen waarin je parameters kunt
toevoegen.

-
-
Je kunt deze parameters in het class diagram echter niet zien. In de code
zie je de parameters wel.
-
-
Om de parameters van de operaties ook in het diagram zichtbaar te maken moet
je op het knopje Diagram View Management van het
Designer window klikken.
-
 |
--- |
 |
Together 6.1 |
|
Together 6.2 |
-
-
Zet vervolgens het Diagram detail level op
Implementation.

-
-
Je kunt nu de parameters van de operaties ook zien in het class diagram:

-
-
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 het knopje aan de
rechterkant van het invoerveld type gebruiken
om een zelf gedefinieerd type (class) te selecteren. Dubbelklik op
Model en dubbelklik op
Werknemer.
-
-
Als we de parameter als een const reference willen meegeven (om een kopietje
te voorkomen) dan moeten we dat in het type veld
wijzigen.

-
-
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 op het knopje
New Diagram in het
Designer window.

-
-
Kies Sequence en vul als Diagram
name in: printSalarissen.

-
-
Maak een actor Boekhouder en een object
THR van de class
Bedrijf. Gebruik de
Inspector om de name
en de insantiates (classname) van het object in
te vullen. Gebruik het knopje rechts van het invoerveld
insantiates om een class te selecteren (dan kun
je geen typefouten maken).
-
-
Klik op het knopje Message. Klik vervolgens op
de balk onder Boekhouder en hou de muisknop vast.
Beweeg de muis nu richting THR en laat de muis
los onder dit object.

-
-
Gebruik het knopje rechts naast het veld operation
in de Inspector om de operatie
printSalarisOverzicht te selecteren.

-
-
Klik vervolgens op het knopje Statement Block
en klik op het balkje waar printSalarisOverzicht
naar wijst. Kies for en type als conditie
alle werkers in.
-
Voeg zelf een object werker van de class
Werknemer aan het sequence diagram toe. Verstuur
nu vanuit de for de messages
geefNaam en geefSalaris
naar de Werknemer
werker.

-
-
Klik rechts op het sequence diagram en kies de menu optie
Generate Implementation. De gegenereerde code
moet nog behoorlijk worden aangepast...

-
-
Je kunt het sequence diagram ook eenvoudig omzetten naar een collaboration
diagram. Klik rechts op het sequence diagram en kies de menu optie
Show as Collaboration.

-
-
Kies File, Save All. Start Borland C++ via het start menu.
Maak een nieuwe console applicatie. Kies
File, New,
Other... kies vervolgens Console
Wizard en selecteer C++ en vink
Console Application aan (en de rest uit).
Kies File, Save Project
As... en save het cpp bestand onder de naam
main.cpp en het project onder de naam
salarisadmin.bpr in het directory
src van het Togetherproject. Voeg de files
Werknemer.cpp en
Bedrijf.cpp aan het project toe. Bekijk de structuur
van het project door het menu View,
Project Manager te kiezen.

-
-
Vul de volgende testcode in
main.cpp
in:
#include "Werknemer.h"
#include "Bedrijf.h"
int main() {
Werknemer harry("Harry Broeders", 4023.23);
Werknemer john("John Visser", 4078.34);
Bedrijf thr;
thr.neemInDienst(harry);
thr.neemInDienst(john);
thr.printSalarisOverzicht();
cin.get();
return 0;
}
-
Vul de code in
Bedrijf.cpp
aan:
#include "Bedrijf.h"
#include "Werknemer.h"
void Bedrijf::neemInDienst(Werknemer& w) {
werkers.push_back(&w);
}
void Bedrijf::printSalarisOverzicht() const {
for (vector<Werknemer*>::const_iterator i(werkers.begin()); i!=werkers.end(); ++i) {
cout<<(*i)->geefNaam()<<" : "<<(*i)->geefSalaris()<<endl;
}
}
Let op! De parameter van de memberfunctie
neemInDienst
is veranderd van const
Werknemer&
in Werknemer&
. Dit was nodig omdat
anders een compilatiefout ontstaat omdat de vector
werkers
objecten van het type Werknemer*
bevat.
Bij nader inzicht klopt dat ook want als een Bedrijf
een
Werknemer
in dienst neemt dan kan die Werknemer
daardoor natuurlijk best veranderen.
-
Vul de code in
Werknemer.cpp
aan:
#include "Werknemer.h"
Werknemer::Werknemer(string n, double s): naam(n), salaris(s) {
}
string Werknemer::geefNaam() const {
return naam;
}
double Werknemer::geefSalaris() const {
return salaris;
}
-
Vul de code in
Bedrijf.h
aan door de juiste include statements
toe te voegen.
#ifndef BEDRIJF_H
#define BEDRIJF_H
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#include "Werknemer.h"
class Bedrijf {
public:
void neemInDienst(Werknemer& w);
void printSalarisOverzicht() const;
private:
/** @link aggregation
* @label heeft in dienst*/
vector <Werknemer*> werkers;
};
#endif //BEDRIJF_H
Let op! De parameter van de memberfunctie
neemInDienst
is hier ook veranderd van const
Werknemer&
in Werknemer&
.
-
Vul de code in
Werknemer.h
aan door de juiste include statement
toe te voegen.
#ifndef WERKNEMER_H
#define WERKNEMER_H
#include <string>
using namespace std;
class Werknemer {
public:
Werknemer(string n, double s);
double geefSalaris() const;
string geefNaam() const;
private:
string naam;
double salaris;
};
#endif //WERKNEMER_H
-
Compileer en run het programma en controlleer 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" tussen Borland C++ Builder en Borland Together ControlCentre
goed op dat je eerst alle bestanden opslaat. Bij beide paketten kan dit met
de sneltoets Ctrl+Shift+s. Als je daarna overschakeld
zal het andere paket automatisch synchroniseren.
We bedenken dat er verschillende soorten werknemers kunnen zijn:
-
Free lance werknemers.
Een FreeLancer
verdient een vast bedrag per gewerkt uur.
-
Vaste werknemers.
Een VasteKracht
verdient een vast maandsalaris.
Opdracht 4a.
Maak in Together 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 het vakje
Abstract aan te vinken. De constructor van
Werknemer heeft nog maar 1 parameter (van het type
string ). 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 waarin het maandsalaris wordt opgeslagen.
-
een overridden memberfunctie
geefSalaris . Deze functie moet
het maandsalaris teruggeven.
Geef de class FreeLancer :
-
een constructor waarin de naam en het uurloon worden doorgegeven.
-
een private variabele waarin het uurloon wordt opgeslagen.
-
een memberfunctie
declareerUren waarmee het aantal gewerkte
uren (een geheel getal) gedeclareerd kan worden.
-
een private variabele waarin het aantal gewerkte uren wordt opgeslagen.
-
een overridden memberfunctie
geefSalaris . Deze functie moet
het maandsalaris als volgt berekenen: salaris = uurloon * aantal gewerkte
uren.
Als je het goed doet ziet het class diagram er ongeveer als volgt uit:
Schakel over naar Borland C++ Builder (vergeet niet om eerst op Ctrl+Shift+s
te drukken) 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 waar nodig aan. Vergeet
ook niet om de juiste include statements toe te voegen.
Vul de volgende testcode in main.cpp in:
#include "Bedrijf.h"
#include "VasteKracht.h"
#include "FreeLancer.h"
int main() {
Bedrijf thr;
VasteKracht harry("Harry Broeders", 4023.23);
thr.neemInDienst(harry);
VasteKracht john("John Visser", 4078.34);
thr.neemInDienst(john);
FreeLancer beun("Beun de Haas", 60.00);
thr.neemInDienst(beun);
beun.declareerUren(120);
thr.printSalarisOverzicht();
cin.get();
return 0;
}
De juiste uitvoer is:
|
Omdat kwartaal 2 dit schooljaar (2009/2010)
slechts uit 6 lesweken bestaat zijn opdrachten 4b en 4c dit jaar niet
verplicht!
We gaan het model en het programma nu nog verder uitbreiden.
We voegen een nieuwe soort werknemer 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 aggregatie met
Werknemer om aan te geven dat een
Manager aan 1 of meer Werknemer s leiding geeft.
-
een overridden memberfunctie
geefSalaris . Deze functie moet
het maandsalaris berekenen volgens de hierboven gegeven formule.
-
een memberfunctie
geefLeidingAan waarmee een
Werknemer ondergeschikt aan een Manager gemaakt
kan worden.
Als je het goed doet ziet het class diagram er ongeveer als volgt uit:
Schakel over naar Borland C++ Builder (vergeet niet om eerst op Ctrl+Shift+s
te drukken) en voeg het bestand Manager.cpp toe aan het project.
Vul de code van Manager waar nodig aan. Vergeet ook niet om
de juiste include statements toe te voegen.
Vul de volgende testcode in main.cpp in:
#include "Bedrijf.h"
#include "VasteKracht.h"
#include "FreeLancer.h"
#include "Manager.h"
int main() {
Bedrijf thr;
VasteKracht harry("Harry Broeders", 4023.23);
thr.neemInDienst(harry);
VasteKracht john("John Visser", 4078.34);
thr.neemInDienst(john);
FreeLancer beun("Beun de Haas", 60.00);
thr.neemInDienst(beun);
beun.declareerUren(120);
Manager johan("Johan de OpleidingsManager");
thr.neemInDienst(johan);
johan.geefLeidingAan(harry);
johan.geefLeidingAan(john);
Manager roelof("Roelof de Directeur");
thr.neemInDienst(roelof);
roelof.geefLeidingAan(johan);
roelof.geefLeidingAan(beun);
thr.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 werknemer 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 aggregatie 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.
Schakel over naar Borland C++ Builder (vergeet niet om eerst op Ctrl+Shift+s
te drukken) en voeg het bestand ManagerAssistent.cpp toe aan
het project.
Vul de code van ManagerAssistent waar nodig aan. Vergeet
ook niet om de juiste include statements toe te voegen.
Vul de volgende testcode in main.cpp in:
#include "Bedrijf.h"
#include "VasteKracht.h"
#include "FreeLancer.h"
#include "Manager.h"
#include "ManagerAssistent.h"
int main() {
Bedrijf thr;
VasteKracht harry("Harry Broeders", 4023.23);
thr.neemInDienst(harry);
VasteKracht john("John Visser", 4078.34);
thr.neemInDienst(john);
FreeLancer beun("Beun de Haas", 60.00);
thr.neemInDienst(beun);
beun.declareerUren(120);
Manager johan("Johan de OpleidingsManager");
thr.neemInDienst(johan);
johan.geefLeidingAan(harry);
johan.geefLeidingAan(john);
Manager roelof("Roelof de Directeur");
thr.neemInDienst(roelof);
roelof.geefLeidingAan(johan);
roelof.geefLeidingAan(beun);
ManagerAssistent annelies("Annelies Nelissen", johan);
thr.neemInDienst(annelies);
thr.printSalarisOverzicht();
cout<<"Annelies maakt promotie!"<<endl;
annelies.assisteert(roelof);
thr.printSalarisOverzicht();
cin.get();
return 0;
}
De juiste uitvoer is:
|
Ga eventueel verder met opgave 5 (niet verplicht)...