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.

  1. Start Together via het start menu.
  2. Kies de menuoptie File, New Project...
  3. Vul de Project name in, kies een Location en selecteer C++ als Default language.


    -
  4. Klik op het Class knopje.


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


    -
  6. Voeg ook de class Werknemer toe aan het diagram.
  7. Klik op het Aggregation knopje.


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


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


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


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


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


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


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


    -
  15. Geef de class Werknemer ook een attribute salaris van het type double.
  16. 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).


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


    -
  18. Je kunt deze parameters in het class diagram echter niet zien. In de code zie je de parameters wel.

    -

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

    -

  20. Zet vervolgens het Diagram detail level op Implementation.


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


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

    -

  23. Als we de parameter als een const reference willen meegeven (om een kopietje te voorkomen) dan moeten we dat in het type veld wijzigen.


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

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


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


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

    -

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


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


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

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


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


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


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


    -
  35. 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;
    }
    
  36. 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.

  37. 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;
    }
    
  38. 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&.

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

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:

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

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