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.

  1. Start Visual Paradigm for UML 10.0 via het start menu.
  2. Kies de menuoptie File, New Project
  3. Selecteer C++ als Language en vul een Project name in. Klik vervolgens op Create Blank Project:


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


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


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


    -
  9. 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.


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


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


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


    -
  14. Vul vervolgens de rol naam werkers in.


    -
  15. 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.

    vp_stap14a
    -
  16. 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.

    vp_stap14b
    -
  17. De associatie ziet er nu als volgt uit:

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


    -
  19. 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.
  20. 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.
  21. 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.


    -
  22. 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.


    -
  23. 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.

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


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


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


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


    -
  28. 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.


    -
  29. 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 &.


    -
  30. 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).


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

    -
  32. 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.


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


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


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


    -
  36. 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.


    -
  37. 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.


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


    -
  39. 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.


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


    -
  41. 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:


    -
  42. 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.


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


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


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


    -
  46. Klik op OK en klik vervolgens op Generate.
  47. 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.


    -
  48. 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.


    -
  49. 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:

  50. 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.
  51. 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;
    }
  52. 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;
        }
    }
  53. 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) {
    }
  54. 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:

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:

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 Werknemers 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:

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)...