[Alg] Business Object met lege ctor

Pagina: 1
Acties:

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 11:13

pjvandesande

GC.Collect(head);

Topicstarter
Het verhaal:
Je hebt een Business Object met een aantal members die nooit null of empty mogen zijn. Waar dus de waarde aan een bepaalde waarde moet voldoen.

Zorg je er dan voor dat dit ook nooit voor kan komen, door bij alle wegen naar de member toe een controle poortje te zetten?

Voorbeeldje, waar ID en Naam nooit leeg zijn:
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
public class Klant
{
    private int _id;
    private string _naam;
    private string _note;
    
    public int ID
    {
        get
        {
            return _id;
        }
    }
    
    public string Naam
    {
        get
        {
            return _naam;
        }
        set
        {
            if( value == null || value == string.Empty )
                throw new ArgumentException( "Could not be null or emtpy." );
                
            _naam = naam;
        }
    }
    
    public string Note
    {
        get
        {
            return _note;
        }
        set
        {
            _note = value;
        }
    }
    
    public Klant( int id, string naam, string note )
    {
        if( naam == null || naam == string.Emtpy )
            throw new ArgumentException( "Naam", "Coult not be null or empty" );
            
        _id = id;
        _naam = naam;
        _note = note;
    }
}


Maar waar wil je nou naartoe?
Waar ik eigelijk naartoe wil is dat ik altijd deze manier hanteer. Dus ik zet bij alle wegen een controle poortje.
Het grootste nadeel hiervan is dat je geen lege ctor hebt, omdat nou eenmaal (in dit voorbeeld) ID en Naam nooit leeg kunnen zijn, er geen leeg Business Object aangemaakt kan worden.

Een leeg Business Object kunnen een hoop voordelen bieden, klein voorbeeldje:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
private void cmdNewKlant_Click( object sender, EventArgs e )
{
    Klant newKlant = new Klant();
    
    using( FrmNewKlant frmNewKlant = new FrmNewKlant( newKlant ) )
    {
        if( frmNewKlant.ShowDialog( this ) == DialogResult.OK )
        {
            _klantRepository.Add( newKlant );
        }
    }
}


Zonder lege ctor kun je nooit een Klant aan je KlantRepository.Add geven, omdat je anders altijd in de ctor het ID al geset moet hebben.

Ik zie ook wel is dat Business Object een internal ctor hebben, zodat je ze niet eens zelf kan instantieren maar het altijd via de Repository moet doen.

Wat is nou je vraag in het kort?
Mag een Business Object een lege/default ctor hebben terwijl er bepaalde members zijn die altijd geset moeten zijn?

[ Voor 7% gewijzigd door pjvandesande op 26-09-2005 21:58 ]


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Lastige vraag. Voor business objecten vind ik hiet niet zo`n groot probleem. Als je de inhoud uiteindelijk maar controleert, maar ik gebruik ze om formulieren bv op in te vullen (en dan weet je van te voren ook niet alles).

Bij Spring gebruiken ze setters/getters/lege constructors bijna overal en dat is iets waar ik wel een probleem mee heb. Ik kan de garantie geven (ik werk liever met een constructor) dat mijn objecten per definitie consistent zijn en verder hoef ik me ook niet druk te maken om alle problematiek die een setter kan veroorzaken. Dus het hangt een beetje van de stroming af denk ik :) Maar bij business objecten (ik neem aan dat je bedoelt domain objecten.. vervelend ook dat er zoveel namen voor zijn) vind ik het niet zo`n probleem.

[ Voor 11% gewijzigd door Alarmnummer op 26-09-2005 22:03 ]


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 11:13

pjvandesande

GC.Collect(head);

Topicstarter
Alarmnummer schreef op maandag 26 september 2005 @ 22:02:
Lastige vraag. Voor business objecten vind ik hiet niet zo`n groot probleem. Als je de inhoud uiteindelijk maar controleert, maar ik gebruik ze om formulieren bv op in te vullen (en dan weet je van te voren ook niet alles).

Bij Spring gebruiken ze setters/getters/lege constructors bijna overal en dat is iets waar ik wel een probleem mee heb. Ik kan de garantie geven (ik werk liever met een constructor) dat mijn objecten per definitie consistent zijn en verder hoef ik me ook niet druk te maken om alle problematiek die een setter kan veroorzaken.
Als je met setters/getters/lege constructors kun je die garantie toch juist niet geven.

Hoewel setters niet zo'n probleem zijn, die kun je namelijk nog controleren. Het is vooral de lege ctor imho.
Setters kun je zoals ik in het voorbeeld aangeeft controleren en eventueel een Exception gooien als de value niet valid is.
Alarmnummer schreef op maandag 26 september 2005 @ 22:02:
Maar bij business objecten (ik neem aan dat je bedoelt domain objecten.. vervelend ook dat er zoveel namen voor zijn) vind ik het niet zo`n probleem.
Toelichting ;)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

questa schreef op maandag 26 september 2005 @ 22:09:
Als je met setters/getters/lege constructors kun je die garantie toch juist niet geven.
Dat klopt. Ik denk dat mijn verhaal dan niet duidelijk over is gekomen, maar we bedoelen idd hetzelfde.
Hoewel setters niet zo'n probleem zijn, die kun je namelijk nog controleren.
Ik vind setters een enorm groot probleem. In sommige gevallen zijn de consequenties niet zo groot, maar er zijn ook gevallen te bedenken waar je eigelijk een extreem gecompliceerde situatie creeert als je alles consistent wil houden en dit is totaal overbodig als je het met een constructor had gedaan. Ik heb hier trouwens niet zo veel last van bij domain objecten, maar meer bij allerlei service objecten (bv met threadpools die je dan ineens instelbaar zou moeten maken).
Het is vooral de lege ctor imho.
Setters kun je zoals ik in het voorbeeld aangeeft controleren en eventueel een Exception gooien als de value niet valid is.
Yep.

Ik zou me bij domain objecten in ieder geval niet zo druk om maken.
Toelichting ;)
In de hoek waar ik rond hang (voornamelijk java/j2ee) noemen zit dit domain objecten. Objecten uit het domein: Fiets, Persoon, Order etc.

[ Voor 9% gewijzigd door Alarmnummer op 26-09-2005 22:14 ]


  • Orphix
  • Registratie: Februari 2000
  • Niet online
questa schreef op maandag 26 september 2005 @ 21:49:
Mag een Business Object een lege/default ctor hebben terwijl er bepaalde members zijn die altijd geset moeten zijn?
Naar mijn mening wel. Je software (service of dal laag) moet echter op momenten wanneer dit echt noodzakelijk is de validiteit kunnen controleren. Een altijd consistent object is mooi om na te streven maar niet altijd mogelijk of praktisch. Een databaseserver zal bij het toevoegen van data op bepaalde momenten ook intern incosistent zijn.

Meestal komt het er dus op neer dat je domeinobjecten de intelligentie bezitten om van zichzelf aan te geven of ze valide zijn. Wanneer bijvoorbeeld dit object wordt opgeslagen in een database controleert de service layer dit, en gaat afhankelijk van de status door met het opslaan, of gooit een foutmelding.

Natuurlijk is het het beste dat je je applicatie zodanig inricht dat de momenten waarop een object eventueel incosistent kan zijn zo klein mogelijk is. Dus alleen bij het invoeren van nieuwe gegevens bijvoorbeeld. En dat de rest van de applicatie altijd met valide objecten werkt.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Orphix schreef op maandag 26 september 2005 @ 22:38:
[...]

Naar mijn mening wel. Je software (service of dal laag) moet echter op momenten wanneer dit echt noodzakelijk is de validiteit kunnen controleren. Een altijd consistent object is mooi om na te streven maar niet altijd mogelijk of praktisch. Een databaseserver zal bij het toevoegen van data op bepaalde momenten ook intern incosistent zijn.
Dat ligt er dus maar aan. De handelingen op de data zijn isolated (althans.. als je dat zo in hebt gesteld) en niemand zal aan de buitenkant zien dat een record tijdelijk corrupt is. Verder zal deze corrupte data ook niet in de db blijven staan op het moment dat de transactie faalt (om wat voor reden dan ook) en een rollback wordt uitgevoerd.

Dat is dus wel een redelijk verschil tov het java geheugen aangezien je daar dus geen garanties krijgt over de consistentie van het object.

[ Voor 9% gewijzigd door Alarmnummer op 26-09-2005 22:47 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 14:12
Mja, het probleem dat je hier een beetje hebt, is dat je die check op verschillende plaats in je object gaat verspreiden. (Je kan er natuurlijk wel een private member oid van maken).

Echter, wat je ook kan doen, is een Validate() method maken (of een IsValid property), die je kunt aanroepen, en waar je dergelijke checks gaat gaan doen.

Of je kan ook, ipv direct een exceptie te gooien (duur), een soort 'rule-systeem' maken. Je maakt een class 'Rule', en een collection, waar je dergelijke 'regels' kunt aan toevoegen.
In je validate method of isvalid property, kan je dan gaan checken of er regels zijn in die collectie die 'gebroken' zijn. (Check het CSLA framework van Rockford Lothka hiervoor eens).

Hmm, ik zie nu dat dit niet echt een antwoord op je vraag is. :+
Tja, aan de ene kant neig ik er toch voor om, voor waardes die echt verplicht zijn, deze al in de constructor een waarde te geven.
Als je echt een leeg object wilt kunnen maken, dan zou je evt kunnen overwegen om een private constructor te hebben zonder argumenten, die je dan, in één of andere factory dmv reflection aanroept. Echter, in jouw voorbeeld zou dat wel eens problemen kunnen opleveren.
Daarom zou ik, zoals ik al eerder zei, niet direct een exceptie gooien, maar eerder werken met een Validate() method oid die je dan aanroept voor je het object bv gaat saven.

https://fgheysels.github.io/


  • Orphix
  • Registratie: Februari 2000
  • Niet online
Alarmnummer schreef op maandag 26 september 2005 @ 22:45:
Dat ligt er dus maar aan. De handelingen op de data zijn isolated (althans.. als je dat zo in hebt gesteld) en niemand zal aan de buitenkant zien dat een record tijdelijk corrupt is. Verder zal deze corrupte data ook niet in de db blijven staan op het moment dat de transactie faalt (om wat voor reden dan ook) en een rollback wordt uitgevoerd.

Dat is dus wel een redelijk verschil tov het java geheugen aangezien je daar dus geen garanties krijgt over de consistentie van het object.
Dat klopt. Daarom zette ik er tussen haakjes bij dat het intern is. Het is maar op welk niveau je het bekijkt. Als gebruiker van een database is die altijd consistent, als ontwikkelaar van de database server zeker niet.

Zoals je zegt is dat bij je eigen java programmatuur niet zo. Maar je kan jezelf wel beschermen door de applicatie dusdanig vorm te geven dat je in 95% van de code gewoon werkt met gevalideerde objecten. Dat je bijvoorbeeld na elke nieuwe invoer of wijziging direct het object persistent opslaat wat ook inhoudt dat het object gevalideerd wordt. Code die gebruik maakt van deze objecten mag deze dus enkel benaderen via de service of dal laag. Zolang nieuwe of gewijzigde objecten niet worden rondgegooid binnen de applicatie zelf hoeft dat wat de TS wil geen probleem te zijn. Maar ik geloof dat jij die mening ook deelde ;)

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 11:13

pjvandesande

GC.Collect(head);

Topicstarter
Als je je objecten consistent maakt, kun je geen lege objecten meer aanmaken. Een klant vanuit een Form aanmaken is eigelijk niet meer mogelijk, als je iig het Adden van je Klant niet aan het Form overlaat, iets wat ik nooit doe.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private cmdNewKlant( object sender, EventArgs e )
{
    using( FrmNewKlant frmNewKlant = new FrmNewKlant() )
    {
        if( frmNewKlant.ShowDialog( this ) == DialogResult.OK )
        {
            // Het form heeft een member waarvan het ID niet geset is.
            // Dit is dus alleen mogelijk als je een lege ctor toestaat.
            Klant newKlant = frmNewKlant.Value;
            
            _klantRepository.Add( newKlant );
            AddKlantToGUI( newKlant );
        }
    }
}


Zonder lege ctor is dit niet mogelijk omdat je FrmNewKlant een Klant object kan returnen waar het ID niet van geset is. Dus zou je het zo moeten doen:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
private cmdNewKlant( object sender, EventArgs e )
{
    using( FrmNewKlant frmNewKlant = new FrmNewKlant() )
    {
        if( frmNewKlant.ShowDialog( this ) == DialogResult.OK )
        {
            Klant newKlant = _klantRepository.Add( frmNewKlant.KlantID,
                        frmNewKlant.KlantNaam, frmNewKlant.KlantNote );
                        
            AddKlantToGUI( newKlant );
        }
    }
}


Het nadeel hiervan is dat je Klant (of ander object) een hoop properties heeft, wat weer lijd tot onleesbare code.
Je kunt ook een ctor maken voor de Klant waarbij alle verplichte moeten worden geset, in dit geval naam, behalven het ID. Het ID word in die ctor met DbHelper.UnsavedID geinstantieerd.
Alarmnummer schreef op maandag 26 september 2005 @ 22:13:
[...]

In de hoek waar ik rond hang (voornamelijk java/j2ee) noemen zit dit domain objecten. Objecten uit het domein: Fiets, Persoon, Order etc.
Dus bij dit soort objecten pas je het niet toe, daar sta je het dus wel toe een lege ctor toe?

Waar controleer je dan of je object wel valid is? Want je wilt niet de checks overal en nergens hebben staan.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

questa schreef op dinsdag 27 september 2005 @ 08:56:
Dus bij dit soort objecten pas je het niet toe, daar sta je het dus wel toe een lege ctor toe?
Bij domain objecten ben ik wat relaxter tov de regels. Een lege constructors + setters is daar standaard.
Waar controleer je dan of je object wel valid is? Want je wilt niet de checks overal en nergens hebben staan.
Dat ligt er een beetje aan. Bij webpages doen we het na submitten van een formulier. Alle informatie invullen op het domain object, en op het moment dat er iets niet deugt kan het object niet gepersist worden.

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 11:13

pjvandesande

GC.Collect(head);

Topicstarter
whoami schreef op maandag 26 september 2005 @ 22:53:
Mja, het probleem dat je hier een beetje hebt, is dat je die check op verschillende plaats in je object gaat verspreiden. (Je kan er natuurlijk wel een private member oid van maken).

Echter, wat je ook kan doen, is een Validate() method maken (of een IsValid property), die je kunt aanroepen, en waar je dergelijke checks gaat gaan doen.

Of je kan ook, ipv direct een exceptie te gooien (duur), een soort 'rule-systeem' maken. Je maakt een class 'Rule', en een collection, waar je dergelijke 'regels' kunt aan toevoegen.
In je validate method of isvalid property, kan je dan gaan checken of er regels zijn in die collectie die 'gebroken' zijn. (Check het CSLA framework van Rockford Lothka hiervoor eens).
Is zeker een goede optie, ik ga dat CSLA Framework is even inspecteren.
whoami schreef op maandag 26 september 2005 @ 22:53:
Hmm, ik zie nu dat dit niet echt een antwoord op je vraag is. :+
Maar wel een intressante toevoeging. ;)
Alarmnummer schreef op dinsdag 27 september 2005 @ 09:01:
[...]

Bij domain objecten ben ik wat relaxter tov de regels. Een lege constructors + setters is daar standaard.
Geef je ze nog wel een IsValid propertie oid?
Alarmnummer schreef op dinsdag 27 september 2005 @ 09:01:

[...]

Dat ligt er een beetje aan. Bij webpages doen we het na submitten van een formulier. Alle informatie invullen op het domain object, en op het moment dat er iets niet deugt kan het object niet gepersist worden.
Ik zou ze in mijn geval op in de Repository controleren als ik inconsistente objecten zou hebben. Alleen houd ik de checks dan in me Repository of in het Domain Object.

Geef ik me Domain Object een method, Validate die dan ArgumentException's gooi oid. Dit is iets wat die meneer op IRC aandraagde.
Ik vind het een goede methode, want zo houd je iig je checks binnen je Domain Object en dmv van ArgumentException's geef je aan wat er precies mist. Ik moet alleen het verhaal wat whoami aandraagde nog even bekijken. Misschien bied dat hele anderen mogelijkheden.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

questa schreef op dinsdag 27 september 2005 @ 09:13:
[...]
Ik zou ze in mijn geval op in de Repository controleren als ik inconsistente objecten zou hebben.
Ik denk dat het taak van je business layer (domain layer) is om validatie af te dwingen aangezien er wel vrij complexe logica in kan komen. Bv.. loon van werknemer mag nooit hoger zijn dan loon directeur.

In principe moeten alle wijzigingen op domain objecten plaatsvinden in de business laag aangezien je dan ook veel beter acties erop kunt ondernemen. Bv.. als het salaris van een werknemer hoger is dan van de directeur, dan moet de directeur een email krijgen met wie te ontslaan. Op het moment dat je toelaat dat in alle lage domain objecten aangepast mogen worden, kan je dit soort logica niet meer inbouwen.

Je ziet deze lagen dan ook regel matig terug:

-------------
service layer (verantwoordelijk coordinatie transacties/security)
-------------
application layer (bv verantwoordelijk voor het versturen van email)
-------------
domain layer (verantwoordelijk voor de wijzigingen op domain objecten.. bv salaris mag niet neg zijn)
-------------

Meestal plet ik die lagen tot een laag *heeft wel iets beters te doen met zijn tijd dan niets doende lagen te schrijven*

Alleen ben ik vaak wat 'relaxter' met deze regels bij kleine webapplicaties. En verder.. ik heb eerlijk gezegd nog niet vaak zo`n puinzooi aangetroffen als bij dit geneuzel. Het groote probleem is dat er te veel geschreven wordt. Te veel informatie kan soms ook tot chaos leiden.

[ Voor 22% gewijzigd door Alarmnummer op 27-09-2005 10:10 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 14:12
Alarmnummer schreef op dinsdag 27 september 2005 @ 10:05:
[...]

Ik denk dat het taak van je business layer (domain layer) is om validatie af te dwingen aangezien er wel vrij complexe logica in kan komen. Bv.. loon van werknemer mag nooit hoger zijn dan loon directeur.
Hoe kan je dat in je 'domein object' zelf doen ? Ik weet het, het is wel een voorbeeld, maar in dit geval kan je dat imo niet in je domein object zelf doen. Je gaat hier nl. een regel gaan checken die verschillende domein objecten beslaat. Dit zou dus willen zeggen dat je domeinobject het loon van de directeur moet kennen (of moet ophalen). Ugly, vind je niet ?

(Trouwens, waarom zou de repository ook geen deel kunnen uitmaken van je domein-model ? Je repository ontvangt domein-objecten, en persist ze. IMO kan een repository gewoon een deel zijn van je domein model).

https://fgheysels.github.io/


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

whoami schreef op dinsdag 27 september 2005 @ 10:11:
[...]

Hoe kan je dat in je 'domein object' zelf doen ?
Tja. ik denk niet dat je dit wilt in je domain object Persoonlijk heb ik een beetje schijt (ongeveer een 500 kg) aan fowler zijn AnemicDomainModel kritiek en gevolg is dat mijn domain objecten niets anders zijn dan records. Tot op heden heb ik er weinig problemen mee gehad en zijn manier van werken vind ik enorm gecompliceerd omdat je echt lastige objecten krijgt. Deze logica plaats ik dus niet in de records, maar in de wijzig functies. Je hebt dus een Manager/Service voor Personeel:

code:
1
2
3
4
5
6
7
8
9
10
11
12
class PersoneelService{

     void wijzigSalaris(Werknemer werknemer, int salaris){
           Werknemer directeur = werknemerDao.findDirecteur();
           if(directeur.getSalaris()<salaris){
                 sendEmailTo(directeur," ontsla: "+werknemer);
           }else{
                werknemer.setSalaris(salaris);
           }
          werknemerDao.save(werknemer);
     }
}


[edit]
Ik moet er wel bij vertellen dat dit soort ellende niet mijn dagelijkse bezigheid is. Dus tot zover heeft het voor mij altijd goed gewerkt, maar dat wil niet zeggen dat dit de beste manier is.

[ Voor 10% gewijzigd door Alarmnummer op 27-09-2005 10:34 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 14:12
Alarmnummer schreef op dinsdag 27 september 2005 @ 10:17:
[...]

Tja. ik denk niet dat je dit wilt in je domain object
Dat dacht ik ook ja, alleen, het kwam wel zo over in jouw post dat je dit in je domain object ging plaatsen.

https://fgheysels.github.io/


  • staefke
  • Registratie: December 2003
  • Laatst online: 18-02 08:01
IMHO moeten argumentloze constructors gewoon kunnen. Het gaat om het verschil tussen de fase dat iets opgebouwd wordt en dat het daadwerkelijk gebruikt wordt. Een auto is immers ook niet direct klaar maar als deze gebruikt wordt wil je wel weten dat de verzameling wielen != null is ;)
Het controlleren van de consistentie van een groep objecten (in een eerder voorbeeld werd al het loon van directeuren en werknemers genoemd) kan zeer complex worden en onderdeel uit gaan maken van je business logic en gaat dan verder dan een 'isValid()' methode bij een specifiek object.
Ik kies er dan ook vaak voor om deze business logic in aparte classes af te handelen..

duh ?


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Ik vind het eigenlijk niet logisch.

Je kan je businessobject toch pas maken op het moment dat je alle gegevens hebt ?
De fabriek maakt toch ook pas je auto op het moment dat ze weten welke kleur je wil?
Ze maken toch geen auto, om dan wanneer hij af is, even te herspuiten in het gewenste kleur...
Bovendien bescherm je je tegen jezelf, en tegen anderen die dat kunnen misbruiken.

ik ben eerder geneigd om aan m'n form de interface/instantie van m'n factory (wat et ook is) mee te geven en daar het object aan te maken

[ Voor 16% gewijzigd door H!GHGuY op 27-09-2005 23:34 ]

ASSUME makes an ASS out of U and ME

Pagina: 1