Toon posts:

Meldingen over systeemflow

Pagina: 1
Acties:

Verwijderd

Topicstarter
Stel ik heb de volgende klasse:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
|-----------------|
| Dossier         |
|-----------------|
| - Handtekening  |
| - Bedrag        |
| + CanAfsluiten  |
| + IsAfgesloten  |
|-----------------|
| + Aanmaken      |
| + Fiatteren     |
| + Doorsturen    |
| + Afsluiten     |
|-----------------|



Ik heb dus de klasse Dossier waarmee van alles gedaan kan worden. Bijvoorbeeld door een Business Laag. Zo kan dit dossier worden afgesloten. Daar hangt wel een aantal voorwaarden aan. Daarom wordt eerst gekeken of het dossier wel afgesloten kan worden:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void Afsluiten()
{
   if ( CanAfsluiten )
   {
      DoeDit();
      DoeDat();
      DoeZus();
      DoeZo();
   }
   else
   {
      //return "Afsluiten niet toegestaan, want ...";  
   }
}



Er wordt dus eerst gevraagd of het dossier zichzelf mag afsluiten. Want als er bijvoorbeeld nog geen handtekening van een bepaald persoon is gezet, of het bedrag overschrijdt een bepaalde limiet, of het is nog niet behandeld door de juiste afdeling (en ga zo maar door) dan mag het dossier zichzelf niet afsluiten.

CanAfluisten zou er bijvoorbeeld zo uitzien:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public bool CanAfsluiten()
{
   if ( Handtekening == null )
   {
      return false;
   }
   if ( Bedrag > 100000 )
   {
      return false;
   }

   ...
   ...

   else
   {
      return true; 
   }
}


Maar dat is niet handig!

Want het dossier ( en dus ook de aanroepende code) weet nu alleen maar dat er niet afgesloten mag worden, maar niet waarom.

Er is een aantal oplossingen voor dit probleem:

Optie 1. Ik kan CanAfsluiten exceptions laten throwen waarin staat waarom het niet mag:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public bool CanAfsluiten()
{
   if ( Handtekening == null )
   {
      throw new Exception("Er is nog geen handtekening gezet");
   }
   if ( Bedrag >= 100000 )
   {
      throw new Exception("Het bedrag moet lager dan 100000 zijn");
   }

   ...
   ...

   else
   {
      return true; 
   }
}

Deze oplossing vind ik niet mooi. Ik heb ooit geleerd dat je alleen excepties gebruikt als dingen niet goed gaan of bij onverwacht resultaat. Bovenstaande voorbeeld gaat echter perfect volgens verwachting. Zie het volgende voorbeeld:
Als ik record 1, 2 en 3 heb ik de database en ik zoek naar record 4, dan vind ik niks, precies zoals ik had verwacht. Ik krijg dus geen "NoRecordsFoundException" maar netjes 0 terug of een lege dataset of -row.
Dus als ik een dossier wil afsluiten terwijl er geen handtekening is, dan verwacht ik dat ik het dossier niet mag afsluiten.
Bovendien verdient het geen schoonheidsprijs als ik een boolean gebruik die alleen maar true teruggeeft. De false waarde treedt nooit op omdat er een exception wordt gegooid.


Optie 2:. Ik kan CanAfsluiten een string laten retourneren waarin staat waarom het niet mag:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public string CanAfsluiten()
{
   if ( Handtekening == null )
   {
      return "Mag niet omdat er nog geen handtekening is gezet";
   }
   if ( Bedrag > 100000 )
   {
      return "Mag niet omdat het bedrag te hoog is";
   }

   ...
   ...

   else
   {
      return ""; 
   }
}

Deze optie is al een stuk netter vind ik. Punt is alleen dat de aanroepende code een soort alternatieve errorhandling moet gaan maken om met deze berichten om te gaan.

Hoe gaan jullie met dit soort zaken om? En welke opties zijn er nog meer?

  • bramseltje
  • Registratie: September 2001
  • Laatst online: 14-01 10:34
Wat je bijvoorbeeld zou kunnen doen is de return-type vervangen door een enum. In die enum kun je dan allerlei waardes definieren, die je vervolgens ook weer netjes met hun tekstuele naam kunt vergelijken.


C#:
1
2
3
4
5
6
public enum DossierResult : int {
    GeneralFailure = -1,
    Success = 0,
    NoSignature = 1,
    ToExpensive = 2
}

  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Kun je geen ErrorObject returnen? Dus een object dat alle errors bevat die bij een bepaalde flow zijn voorgekomen. Zoiets:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class SystemErrors {
    private List errors = new ArrayList();
    public void addError(String errorCode, int severity);
    public boolean hasErrors();
}

...

public SystemErrors doBusinessLogic() {
    SystemErrors se = new SystemErrors();
    if (iets) se.addError("error.invalid.username", Severity.ERROR);
    if (iets2) se.addError("error.invalid.password", Severity.ERROR);
    if (iets3) se.addError("error.database.cannot.connect", Severity.FATAL);

    if (hasErrors) return se;
    else return null;
}

of:

public SystemErrors doBusinessLogic() {
    SystemErrors se = new SystemErrors();
    try {
        ... // risky code
    } catch (Exception e) { se.addError("unknown", Severity.FATAL); return se; }
}

Fat Pizza's pizza, they are big and they are cheezy


  • JHS
  • Registratie: Augustus 2003
  • Laatst online: 04-01 15:49

JHS

Splitting the thaum.

JKVA: En hoe zou dit de topicstarter kunnen helpen :? . Volgens mij heeft hij gewoon gelijk in het feit dat je geen exceptions moet gaan gooien als je code iets normaals doet, en of je nu in de code de exceptions gaat afhandelen, of ze aan een ErrorObject toevoegd maakt toch op zich geen bal uit :? .

Aan de andere kant zou je het ook zo kunnen interpreteren: Een client wíl het dossier sluiten, maar mag dat helemaal niet, want [reden], dus dat is wel degelijk een fout. Net zoals iets proberen waar je geen rechten toe hebt ook een error, en lijkt me een exception mag / moet gooien.

Het ligt er in mijn ogen dus aan of de client alleen het dossier zou moeten gaan sluiten als dat mag, of dat altijd mag proberen.

Bramseltje: Het nadeel van die oplossing lijkt me dat je Afsluiten() veel afhankelijker maakt van de implementatie van CanAfsluiten() :) ?

DM!


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

@JHS: Het was meer bedoeld als vervanging van de strings die nu hard in de code geklopt staan. Bovendien kun je met zo'n objectje ook meerdere fouten doorgeven. Kan weleens handig zijn voor zaken waarbij meerdere condities van belang zijn.

Daarnaast ben ik meestal ook voor het gooien van exceptions, (zelfde reden als jij) maar ik wilde er geen wel-niet discussie van maken over exceptions. :)

Fat Pizza's pizza, they are big and they are cheezy


Verwijderd

Ik denk dat je het inderdaad met een enum zou kunnen oplossen; wellicht als volgt:

C#:
1
2
3
4
public bool CanAfsluiten(out "Enum") // geen correcte syntax uiteraard ;P
{
   [..]
}


Dan heb je nog wel die boolean waarde als return-value.

[ Voor 14% gewijzigd door Verwijderd op 02-04-2006 13:23 ]


Verwijderd

Als je Afsluiten() aanroept, verwacht je dat het ook daadwerkelijk afsluit. Als het document niet kan sluiten, klopt die verwachting niet meer en werp je een exception.

  • Orphix
  • Registratie: Februari 2000
  • Niet online
Allereerst: CanAfsluiten? Is het niet mooier om dit KanAfsluiten te noemen?

Maar dat terzijde zou ik het anders aanpakken. Je wilt nu een enkele methode twee verantwoordelijkheden geven:
1) aangeven of je mag afsluiten of niet en
2) in het geval je niet mag afsluiten, wat de foutmelding is

Als je deze twee opsplitst in twee aparte methodes is het probleem veel eenvoudiger, en hoef je niet je foutmeldingen in een onderhoudsgevoelige enum te stoppen.

Dus bijvoorbeeld:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public bool CanAfsluiten()
{
    return GetAfsluitenFoutmelding() == string.Empty;
}

public string GetAfsluitenFoutmelding()
{
   if ( Handtekening == null )
   {
      return "Mag niet omdat er nog geen handtekening is gezet";
   }
   if ( Bedrag > 100000 )
   {
      return "Mag niet omdat het bedrag te hoog is";
   }

   ...
   ...

   else
   {
      return string.Empty; 
   }
}

/* De client */
public void DossierAfsluiten()
{
    if(CanAfsluiten())
    {
        // doe je ding
    }
    else
    {
        MessageBox(GetAfsluitenFoutmelding());
    }
}

  • Orphix
  • Registratie: Februari 2000
  • Niet online
Verwijderd schreef op zondag 02 april 2006 @ 13:52:
Als je Afsluiten() aanroept, verwacht je dat het ook daadwerkelijk afsluit. Als het document niet kan sluiten, klopt die verwachting niet meer en werp je een exception.
Blijkbaar geldt dat niet, want er wordt expliciet hierop gecontroleerd met de methode CanAfsluiten(). Oftewel het zal regelmatig voorkomen dat mensen een dossier proberen te sluiten, die nog niet gesloten mag worden. In dat geval is het beter om in je code expliciet de foutafhandeling te verwerken. Een echte exception zou bijvoorbeeld zijn dat er geen geheugen meer beschikbaar is, of de database connectie is weggevallen.

  • IceM
  • Registratie: Juni 2003
  • Nu online
Moet je CanAfsluiten() niet aanroepen in de Gui code i.p.v. in de dossier code? Ik ben het eens met breezze dat Afsluiten() ook moet afsluiten, de code in de userinterface zal moeten checken of een open dossier afgesloten mag worden of niet, en al dan niet met een messagebox komen.

...


Verwijderd

Topicstarter
IceM schreef op dinsdag 04 april 2006 @ 16:55:
Moet je CanAfsluiten() niet aanroepen in de Gui code i.p.v. in de dossier code? Ik ben het eens met breezze dat Afsluiten() ook moet afsluiten, de code in de userinterface zal moeten checken of een open dossier afgesloten mag worden of niet, en al dan niet met een messagebox komen.
Je kan niet altijd van te voren weten of iets gaat lukken.

Stel het volgende simpele voorbeeld:
Het saldo op mijn rekening is 100,-. Ik mag dus geld opnemen van mijn rekening -> CanGeldOpnemen zou dus true opleveren.

Vervolgens ga ik 200,- opnemen. Maar dat kan niet omdat ik geen negatief saldo mag hebben.

Verwijderd

Topicstarter
Wat ik trouwens ook lastig vind als ik strings ga teruggeven i.p.v. exceptions hoe ga je dat dan afhandelen?

Stel mijn object kan 10 verschillende meldingen teruggeven. Met drie meldingen moet het systeem zelf nog iets gaan afhandelen de overige 7 kunnen aan de gebruiker kenbaar gemaakt zonder dat het systeem nog handelingen uitvoert.

Bijvoorbeeld:

object Dossier.Afsluiten() geeft terug: (onzinnge voorbeelden)
- "Variabele X staat nog niet in juiste stand"
- "Status van dat label moet nog omgezet worden"
- "Actie Y is nog niet uitgevoerd"


Dan moet de aanroepende code (of dat nou een business laag is, of de UI maakt niet zoveel uit, het gaat erom dat de aanroepende code iets moet gaan doen), een bepaalde actie gaan uitvoeren:


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
string result = Dossier.Afsluiten();

if result = "Variabele X staat nog niet in juiste stand"
{
   ZetVariabeleX(waarde);
}
elseif result = "Status van dat label moet nog omgezet worden"
{
   ZetLabelToStatus(dit);
}
elseif result = "Actie Y is nog niet uitgevoerd"
{
   DoeActieY(dat);
}
elseif result = ""
{
   DoFinallySomethingElse
}


Tja, dan kan je net zo goed met exceptions gaan werken.

Ik begin er dus steeds meer voor de voelen om gewoon wel Exceptions te gaan gooien.

  • NDF82
  • Registratie: Januari 2002
  • Laatst online: 10:35

NDF82

Doomed Space Marine

Orphix schreef op zondag 02 april 2006 @ 13:59:
Als je deze twee opsplitst in twee aparte methodes is het probleem veel eenvoudiger, en hoef je niet je foutmeldingen in een onderhoudsgevoelige enum te stoppen.

Dus bijvoorbeeld:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public bool CanAfsluiten()
{
    return GetAfsluitenFoutmelding() == string.Empty;
}

public string GetAfsluitenFoutmelding()
{
   if ( Handtekening == null )
   {
      return "Mag niet omdat er nog geen handtekening is gezet";
   }
   if ( Bedrag > 100000 )
   {
      return "Mag niet omdat het bedrag te hoog is";
   }

   ...
   ...

   else
   {
      return string.Empty; 
   }
}

/* De client */
public void DossierAfsluiten()
{
    if(CanAfsluiten())
    {
        // doe je ding
    }
    else
    {
        MessageBox(GetAfsluitenFoutmelding());
    }
}
Dit doe je in C, niet in C#. In C# gebruik je hiervoor gewoon een Exception.

Pentium 233MHz MMX + Diamond Monster 3D 3DFX Voodoo II


  • IceM
  • Registratie: Juni 2003
  • Nu online
Verwijderd schreef op dinsdag 04 april 2006 @ 23:11:
[...]


Je kan niet altijd van te voren weten of iets gaat lukken.

Stel het volgende simpele voorbeeld:
Het saldo op mijn rekening is 100,-. Ik mag dus geld opnemen van mijn rekening -> CanGeldOpnemen zou dus true opleveren.

Vervolgens ga ik 200,- opnemen. Maar dat kan niet omdat ik geen negatief saldo mag hebben.
Dan moet je dus een functie maken KanGeldOpnemen(double bedrag), programming by contract noemen ze zoiets geloof ik.

...


  • kenneth
  • Registratie: September 2001
  • Niet online

kenneth

achter de duinen

NDF82 schreef op woensdag 05 april 2006 @ 09:26:
[...]


Dit doe je in C, niet in C#. In C# gebruik je hiervoor gewoon een Exception.
Wat is de exceptie dan? Het controleren of het afsluiten mogelijk is, is het doel van de methode, niet de uitzondering.

Look, runners deal in discomfort. After you get past a certain point, that’s all there really is. There is no finesse here.


  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 23-12-2025
Is het opgooien van een AfsluitFoutmelding event geen mogenlijkheid voor in CanAfsluiten? Zo word de afhandeling van de foutmelding buiten je normale flow gedaan en gooi je geen exceptie op omdat er niets uitzonderlijks gebeurd.

  • Orphix
  • Registratie: Februari 2000
  • Niet online
Verwijderd schreef op dinsdag 04 april 2006 @ 23:28:
Wat ik trouwens ook lastig vind als ik strings ga teruggeven i.p.v. exceptions hoe ga je dat dan afhandelen?
Dat is een fout in je code. Niet een fout in de input / van de gebruiker. En dan moet je een Exceptie gooien. Vaak wordt hiervoor een Assert() gebruikt. Asserts moeten gewoon altijd kloppen.
NDF82 schreef op woensdag 05 april 2006 @ 09:26:
Dit doe je in C, niet in C#. In C# gebruik je hiervoor gewoon een Exception.
Het zal een veel voorkomend probleem zijn dat de input niet geldig is. De businesslayer (of service layer) biedt hier aan de UI laag de mogelijkheid om hier netjes op te controleren, en de foutmelding hiervan op te vragen. En daarbij de logica in de businesslayer te houden. Niks mis mee. Uiteraard zal de businesslayer zelf altijd alsnog deze check moeten uitvoeren bij het daadwerkelijke Afsluiten()! De methode Afsluiten() mag er van uit gaan dat alles valide is, en dus een exceptie gooien wanneer dit niet zo is.
PrisonerOfPain schreef op woensdag 05 april 2006 @ 10:22:
Is het opgooien van een AfsluitFoutmelding event geen mogenlijkheid voor in CanAfsluiten? Zo word de afhandeling van de foutmelding buiten je normale flow gedaan en gooi je geen exceptie op omdat er niets uitzonderlijks gebeurd.
Dat het buiten je 'normale' flow wordt gedaan lijkt me niet wenselijk. Zodra je iets wil uitvoeren (als UI code) wil je toch direct weten of het gelukt is of niet?

Verwijderd

Dat is dus een soortgelijk probleem als waar ik vandaag mee aan het stoeien ben geweest.

De oplossing waar ik uiteindelijk voor gekozen heb, maakt gebruik van exceptions. Het object dat aangeroepen wordt gooit een exceptie als iets niet gaat zoals het in de normale situatie zou moeten. Omdat ik de situatie van allerlei error codes die geretourneerd worden wil vermijden (waardoor de code in elke aanroep weer complex wordt), is het gooien van een exceptie in zoverre makkelijk omdat je (in mijn optiek) twee keuzes hebt:

- de exceptie die je wilt tonen aan de gebruiker vang je af in de aanroepende code;
- de excepties die je niet wilt tonen, maar die je wellicht wilt loggen, worden door een globale exception handler afgevangen.

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 23-12-2025
Orphix schreef op woensdag 05 april 2006 @ 19:35:
Dat het buiten je 'normale' flow wordt gedaan lijkt me niet wenselijk. Zodra je iets wil uitvoeren (als UI code) wil je toch direct weten of het gelukt is of niet?
Dan vangt de UI code dat event toch af? Vziw is dat de manier waarop het model communiceerd met de view, door middel van events. En dan weet je toch ook direct of een bepaalde actie al dan niet gelukt is? Of zie ik iets over het hoofd?

  • kenneth
  • Registratie: September 2001
  • Niet online

kenneth

achter de duinen

Vrieler, bij jouw probleem gaat het ook echt om uitzonderingen.
Maar hier is de controle een onderdeel van de flow. En excepties als flow control zijn nou niet echt goede gewoontes :)

Look, runners deal in discomfort. After you get past a certain point, that’s all there really is. There is no finesse here.


  • Orphix
  • Registratie: Februari 2000
  • Niet online
PrisonerOfPain schreef op woensdag 05 april 2006 @ 20:26:
Dan vangt de UI code dat event toch af? Vziw is dat de manier waarop het model communiceerd met de view, door middel van events. En dan weet je toch ook direct of een bepaalde actie al dan niet gelukt is? Of zie ik iets over het hoofd?
Event-driven betekent dat je als client op de hoogte wordt gehouden van acties waar je zelf geen controle over hebt. Bijvoorbeeld een muis-klik. Of het uitschakelen van een netwerkverbinding door een gebruiker.

Maar in dit geval is het gewoon je eigen code. Je weet prima wat je kan verwachten en kan daar direct op reageren. Het toepassen van een event in dit geval volgt niet de filosofie achter event-driven. Daarnaast maakt het het enkel complexer. Zo is het in dit geval bij een event lastig om te bepalen in welke context de foutmelding is gegenereerd. Je zou dan al met state objecten moeten gaan werken. Nodeloos ingewikkeld dus.

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 23-12-2025
Orphix schreef op woensdag 05 april 2006 @ 20:37:
Event-driven betekent dat je als client op de hoogte wordt gehouden van acties waar je zelf geen controle over hebt. Bijvoorbeeld een muis-klik. Of het uitschakelen van een netwerkverbinding door een gebruiker.
Nee. Daarmee limiteer je onnodig de toegevoegde waarde die events kunnen hebben voor je applicatie. Sterker nog het hele MVC gebeuren is er op gebaseerd. (Controller -> Model, Model -> event, event -> View). Sterker nog, dat is precies wat je hier wilt, als de validatie, van in dit geval het Dosier, fout gaat wil je vanuit je Model je View op de hoogte stellen. Aangezien je in je Model geen weet wilt hebben van je View is dit de enige oplossing.
The model should have no direct knowledge of the view. However, the observer pattern can be used to provide some indirection between model and view, allowing the model to notify interested parties of a change.
Daarbij worden events op veel meer verschillende plaatsen gebruikt als je zou verwachtten, zie bijvoorbeeld de SAX parser welke events opgooit voor open tags, close tags, etc.
Maar in dit geval is het gewoon je eigen code. Je weet prima wat je kan verwachten en kan daar direct op reageren. Het toepassen van een event in dit geval volgt niet de filosofie achter event-driven. Daarnaast maakt het het enkel complexer. Zo is het in dit geval bij een event lastig om te bepalen in welke context de foutmelding is gegenereerd. Je zou dan al met state objecten moeten gaan werken. Nodeloos ingewikkeld dus.
De enige `state' die belangrijk is in dit geval is de foutmelding zelf (die wil je ook niet als strings in je Model hebben trouwens, dat is View data). Dus een delegate met een sender en enum parameter aan een AfsluitFout event koppelen is alles. De (verdere) context is dan vrij irrelevant, en zou de View waar nodig, nog verder uit het Model kunnen halen.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
enum AfsluitFoutmelding{
    GeenFout,
    GeenHandtekening,
    BedragTeHoog // Enzovoorts
}
    
class Dossier {
    public string Handtekening;
    public Decimal Bedrag;
    
    public delegate void AfsluitFout(object sender, AfsluitFoutmelding melding);
    public event AfsluitFout OnAfsluitFout;

    private AfsluitFoutmelding KanAfsluiten_(){
        if(Handtekening == null){
            return AfsluitFoutmelding.GeenHandtekening;
        }else if(Bedrag > 10000){
            return AfsluitFoutmelding.BedragTeHoog;
        }
        
        return AfsluitFoutmelding.GeenFout;
    }
    
    public void Afsluiten(){
        AfsluitFoutmelding melding = KanAfsluiten_();
        
        if(melding != AfsluitFoutmelding.GeenFout){
            if(OnAfsluitFout != null){
                OnAfsluitFout(this, melding);
            }
        }else{
            //
            // Sluit het dossier netjes af
            //
        }
    }
}

//
// En in je View
//

private Dossier dossier_ = new Dossier();

public DossierForm() {
    InitializeComponent();
    dossier_.OnAfsluitFout += new Dossier.AfsluitFout(Dossier_Fout);
}

private void Dossier_Fout(object sender, AfsluitFoutmelding melding){
    if(melding == AfsluitFoutmelding.GeenHandtekening){
        MessageBox.Show("Geen handtekening gezet!");
    }else if(melding == AfsluitFoutmelding.BedragTeHoog){
        MessageBox.Show("Te hoog bedrag");
    }
}

private void Afsluiten_Click(object sender, EventArgs e) {
    dossier_.Afsluiten();
}

  • Orphix
  • Registratie: Februari 2000
  • Niet online
PrisonerOfPain schreef op donderdag 06 april 2006 @ 13:46:
Nee. Daarmee limiteer je onnodig de toegevoegde waarde die events kunnen hebben voor je applicatie. Sterker nog het hele MVC gebeuren is er op gebaseerd. (Controller -> Model, Model -> event, event -> View). Sterker nog, dat is precies wat je hier wilt, als de validatie, van in dit geval het Dosier, fout gaat wil je vanuit je Model je View op de hoogte stellen. Aangezien je in je Model geen weet wilt hebben van je View is dit de enige oplossing.
Uiteraard kan je events toevoegen aan je model. Maar: enkel als je software dat nodig heeft! Ik kan mij goed voorstellen dat je in je model een event wil hebben die aangeroepen wordt zodra érgens een exceptie wordt opgegooid. Bijvoorbeeld om in de View deze error te loggen. Zou kunnen. Ik kan me ook voorstellen dat je voor elke methode die je aanroept, de foutcode via een event terug wil laten koppelen. Echter, voordat je dit doet, moet dit wel een meerwaarde hebben boven de procedurele aanpak.

Maak het niet complexer dan het is, tenzij nodig. In dit geval vind ik de meerwaarde niet groot genoeg. Misschien er is maar 1 view ten alle tijden. En misschien willen de andere views niet op de hoogte worden gehouden van de foutmelding.

Het aanroepen van KanAfsluiten() vraagt informatie op van het model. Het wijzigt op geen enkele manier het model zelf. Waarom zouden de andere views hiervan op de hoogte moeten worden gebracht?
Daarbij worden events op veel meer verschillende plaatsen gebruikt als je zou verwachtten, zie bijvoorbeeld de SAX parser welke events opgooit voor open tags, close tags, etc.
Ik heb hier geen directe ervaring mee. Maar ik kan me goed voorstellen dat dit erg handig is voor de client-programmeur om in het parse proces in te kunnen grijpen. Blijkbaar is het parsen een dusdanig belangrijk proces in het model, dat ervoor is gekozen om dit toegangelijk te maken buiten het model. Maar voor veel gevallen is het beter om interne processen gewoon binnen het model te houden. En de views hiervan niet op de hoogte te stellen.
De enige `state' die belangrijk is in dit geval is de foutmelding zelf (die wil je ook niet als strings in je Model hebben trouwens, dat is View data). Dus een delegate met een sender en enum parameter aan een AfsluitFout event koppelen is alles. De (verdere) context is dan vrij irrelevant, en zou de View waar nodig, nog verder uit het Model kunnen halen.
De aanroep is de context. Stel je hebt twee views: A en B. Beiden zijn observer van het model en krijgen een seintje wanneer een fout optreedt.
A roept Afsluiten() maar het model is nog niet valide. Vervolgens laten zowel A en B de foutmelding zien. Vanuit view B gezien hoeft dit helemaal niet logisch te zijn (hoeft, misschien is het wel wenselijk, dit weet je niet!). Met state bedoel ik dus niet enkel de state van het model (deze is immers niet eens gewijzigd), maar ook de context waarin de methode wordt aangeroepen.

Daarnaast heeft het gebruik van enums enkele nadelen t.o.v. excepties. Zo is inheritance, en dus polymorfisme, niet mogelijk. Je bent dus zeer concreet bezig, wat betekent dat je view sterk gekoppeld is aan het model. Dit is niet goed voor de uitbreidbaarheid. Een aanpassing is dan ook bewerkelijk, omdat je bij elke toegevoegde foutmelding dit in zowel het model én alle views moet aanpassen.
.. code ..
Ik zeg dus niet je aanpak per definitie fout is. Maar als ik kijk naar de complexiteit van deze code om simpelweg te bepalen of een dossier afgesloten kan worden vraag ik me af of het niet z'n doel voorbijschiet :)
Pagina: 1