Datamatikeruddannelsen - Softwaredesign

Systemudvikling - tutorial 2

Eksempel på anvendelse af Larmans metode - hvad der skete i projektets 1. dag

I denne tutorial vil vi se på, hvordan anvisningerne fra tutorial 1 kan anvendes til løsning af en konkret opgave fra use case til færdig programkode skrevet i C#. Opgaven består i at udvikle et virksomhedsspil - beskrivelsen af hele opgaven kan ses her.

Første opgave går ud på at vælge nogle passende use cases, som man kan starte med at realisere. Til dette eksempel har jeg valgt at realisere to af de specificerede use cases, Betal leverandører for bestilte varer og Marker nye ordrer hos leverandører. Herudover realiseres en operation, som er hensigtsmæssig i forhold til opstart af spillet, Start forfra.

De tre use cases kræver hver for sig kun en enkelt systemoperation for at kunne blive udført - i praksis udløses en operation ved at brugeren trykker på en knap, aktiverer et menupunkt eller på anden måde aktiverer systemet. Uanset hvilken handling, der er tale om, udløses en event i Windows, som overfører kontrollen til en tilsvarende event handler i vores program.

System Sekvens Diagram

Nedenfor er vist et system sekvens diagram for de tre operationer, som vi vil starte med at realisere:

Domænemodel

Ifølge vores strategi kan vi også på dette tidspunkt give et bud på en domænemodel for de klasser, som vi mener vil være nødvendige for realisering af de tre første operationer. En foreløbig domænemodel er gengivet nedenfor:

Klasserne og attributterne er fundet ved læsning af de to use cases, vi har til rådighed. Her nævnes leverandør og råvarer. Regnskabet nævnes ikke direkte; men kassen omtales og andre steder i opgaveformuleringen kan man læse, at regnskabet bliver vigtigt, så jeg vælger at lave en Regnskab klasse og lade kassen indgå som attribut i denne. Man kunne også overveje at lave kontoklasser og på den måde opbygge et klassisk regnskabssystem. Denne beslutning vælger jeg dog at udskyde til et senere tidspunkt til fordel for den mere simple løsning, som jeg har valgt her.

Sekvensdiagram for sytemoperationen "modtageBestilteVarer"

Denne systemoperation er udledt af use casen Betal leverandører for bestilte varer.

Som det fremgår af diagrammet har jeg tænkt mig at anvende et Controller mønster til håndtering af de forskellige systemoperationer. I første omgang bruger jeg en controller, som svarer godt til, hvad Larman kalder en facade controller. Da der på sigt kommer mange operationer, som denne controller skal kontrollere, indgår det allerede nu i mine overvejelser, om controlleren skal opdeles i flere separate controllere - hver med sit ansvarsområde. I forhold til den foreliggende opgave kunne en opdeling i 4 controllere være en mulighed - disse 4 controllere skulle så opdeles i start år rutiner, kvartalsrutiner, slut år rutiner og rutiner uden tidsmæssig tilknytning. Men i første omgang nøjes jeg med en simpel facade controller.

Den hændelse, der udløser operationen, er tryk på en knap i brugergrænsefladen (UI). Dette tryk aktiverer en eventhandler i Windows programmet, som eventuelt samler de data, der skal overføres som parametre til controlleren - i dette tilfælde skal ingen parametre dog overføres, idet alle nødvendige data allerede skal befinde sig i modellen.

Som det ses i diagrammet forestiller jeg mig, at det bestilte antal hentes fra Leverandørobjektet, hvorefter Råvareobjektet får besked om at modtage det bestilte antal. Prisen beregnes og kasse og råvare konto i Regnskabsobjektet opdateres. Endelig nulstilles antal i Leverandørobjektet. I diagrammet er også medtaget en updateGUI funktion i brugergrænsefladen - denne sørger for at brugergrænsefladens felter bliver opdateret med modellens nye indhold.

Sekvensdiagram for systemoperationen "markerNyeOrdrer"

Denne systemoperation er udledt af use casen Marker nye ordrer hos leverandører.

Som det fremgår af diagrammet, er denne systemoperation opbygget efter samme skabelon som den første systemoperation. Controlleren får kontrollen fra en eventhandler i brugergrænsefladen - denne gang medsendes en parameter, som hentes i indholdet af det felt i brugergrænsefladen, hvor man har indtastet det antal nye råvarer man vil bestille til levering i næste kvartal. Controlleren sender oplysningen videre til Leverandørobjektet. Til slut opdateres indholdet af brugergrænsefladen (updateGUI), så det igen svarer til indholdet af modellens objekter.

Kodning

Leverandør klassen
class Leverandoer
{
  private int markeretBestillingMaster;
  public Leverandoer()
  {
    Master = 0;
  }
  public int Master
  {
    get { return markeretBestillingMaster; }
    set { markeretBestillingMaster = value; }
  }
  public void reset()
  {
    Master = 0;
  }
}
 
Råvare klassen
class Raavare
{
  private int antalMaster;
  private int valueMaster;
  public Raavare()
  {
    antalMaster = 0;
    valueMaster = 0;
  }
  public void modtagMaster(Regnskab regnsk, int antal)
  {
    Master = antal;
    Value = Value + antal * 1;
    regnsk.Kasse = regnsk.Kasse - antal * 1;
    regnsk.Raavarer = regnsk.Raavarer + antal * 1;
  }
  public int Master
  {
    get { return antalMaster; }
    set { antalMaster = value; }
  }
  public int Value
  {
    get { return valueMaster; }
    set { valueMaster = value; }
  }
}
Regnskab klassen
class Regnskab
{
  private int kasse;
  private int raavarer;
  public Regnskab()
  {
    kasse = 0;
    raavarer = 0;
  }
  public int Kasse
  {
    get { return kasse; }
    set { kasse = value; }
  }
  public int Raavarer
  {
    get { return raavarer; }
    set { raavarer = value; }
  }
}
Model klassen

Model klassen er medtaget i henhold til Factory mønsteret som beskrevet af Larman. Mønsteret anvendes, når et antal objekter med fordel kan oprettes ved f.eks. opstart af programmet.

class Model
{
  Leverandoer l;
  Raavare r;
  Regnskab regnsk;
  public Model()
  {
    l = new Leverandoer();
    r = new Raavare();
    regnsk = new Regnskab();
  }
  public Leverandoer getLeverandoer()
  { return l; }
  public Raavare getRaavare()
  { return r; }
  public Regnskab getRegnskab()
  { return regnsk; }
}
Facade controlleren
class Facade
{
  Model m;
  public Facade(Model model)
  {
    m = model;
  }
  public void forfra()
  { // Sæt alle objekter til start situation
    Leverandoer l = m.getLeverandoer();
    l.Master = 2;
    Raavare r = m.getRaavare();
    r.Value = 3;
	Regnskab regnsk = m.getRegnskab();
    regnsk.Kasse = 17;
    regnsk.Raavarer = 3;
  }
  public void modtageBestilteVarer()
  {
    Leverandoer l = m.getLeverandoer();
    int antal = l.Master;
    Raavare r = m.getRaavare();
    Regnskab regnskab = m.getRegnskab();
    r.modtagMaster(regnskab, antal);
    l.reset();
  }
  public void markerNyeOrdrer(int antal)
  {
    Leverandoer l = m.getLeverandoer();
    l.Master = antal;
  }
}
Samspillet mellem modellen og brugergrænsefladen

Modellen og brugergrænsefladen associeres ved starten af programmet. Brugergrænsefladen skal kende facade controlleren, så den kan aktivere de forskellige systemoperationer, og brugergrænsefladen skal kende modellen, så de forskellige modelobjekters indhold kan hentes og udskrives på skærmbilledet. Dette kan gøres ret enkelt ved følgende små modifikationer af den klasse, der indeholder definitionen af de eventhandlere, der er knyttet til skærmbilledet.

public partial class Form1 : Form
{
  private Model m;
  private Facade f;
  public Form1()
  {
    m = new Model();
    f = new Facade(m);
	InitializeComponent();
    UpdateGUI();
  }
Next step knappens eventhandler

Der er kun en enkelt knap til aktivering af næste trin i spillet - til gengæld er der et tekstfelt, som fortæller, hvad det næste trin i processen er. En enkelt event handler kommer altså til at aktivere flere forskellige system operationer.

private void buttonStep_Click(object sender, EventArgs e)
{
  int state = 0;
  if (textAktueltStep.Text == "Modtag bestilte varer") state = 3;
  if (textAktueltStep.Text == "Marker nye ordrer") state = 4;
  if (textAktueltStep.Text == "Opdatering af produktion") state = 5;
  if (textAktueltStep.Text == "Igangsæt ny produktion") state    = 6;
  textAktueltStep.Text = textNextStep.Text;
  switch (state)
  {
    case 3:
    {
      f.modtageBestilteVarer();
      UpdateGUI();
      textNextStep.Text = "Opdatering af produktion";
      textBestillingMaster.ReadOnly = false;
      textBestillingMaster.Text = "";
      textBestillingMaster.Focus();
      break;
    }
    case 4:
    {
      int antal;
      antal = Int32.Parse(textBestillingMaster.Text);
      f.markerNyeOrdrer(antal);
      UpdateGUI();
      textNextStep.Text = "Igangsæt ny produktion";
      textBestillingMaster.ReadOnly = true;
      break;
    }
    case 5:
    {
      textNextStep.Text = "Modtag bestilte varer";
      break;
    }
    case 6:
    {
      textNextStep.Text = "Marker nye ordrer";
      break;
    }
  }
}

Hele Visual Studio projektet kan downloades her.


Oprettet: 8. december 2007
Sidst opdateret: 13. december 2007