[java] constructor design

Pagina: 1
Acties:

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Een slechte gewoonte van mij was dat ik soms objecten maak (en goedkeur) met exceptions van de constructor.

vb
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Persoon 
{
   private int leeftijd;
 
   public Persoon(int leeftijd){
    if(leeftijd<0)
       throw new InvalidArgumentException("leeftijd kan niet kleiner zijn dan 0");
    this.leeftijd = leeftijd;
   }
   
}

Persoon persoon = null
try{
  persoon = new Persoon(leeftijdField.getLeeftijd());
}catch(IllegalArgumentException e){
   ...bericht..
   ...return ofzo.
}

Maar dit is niet een supergoed ontwerp, want je regelt de program flow mbv exception. Je kan ook eerst gaan checken voor je hebt object gaat maken.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Persoon 
{
   public static boolean checkLeeftijd(int leeftijd){
    return leeftijd>=0;
   }

   private int leeftijd;
 
   public Persoon(int leeftijd){
    if(leeftijd<0)
       throw new InvalidArgumentException("leeftijd kan niet kleiner zijn dan 0");
    this.leeftijd = leeftijd;
   }
}

int leeftijd = leeftijdField.getLeeftijd();
Persoon persoon = null;
if(!Persoon.checkLeeftijd(leeftijd))
   ... bericht
   return ofzo..
else{
  persoon = new Persoon(leeftijd);
}

Maar je loopt nu 2 keer te checken en naarmate je meer en lastigere objecten als argument mee geeft dan wordt het er niet sneller op. Wat is hier de juiste aanpak voor? Of moet je in dit geval een uitzondering maken?

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Ik vind dit absoluut geen verkeerd gebruik Exceptions. Dit voorbeeld is wellicht enigszins vreemd, maar in het algemeen mag je best exceptions gooien bij verkeerde parameters.

Je probeert hier immers een object aan te maken met parameters die incorrect zijn. Dat is een programmeerfout en dus mag je daar best een exception gooien.

Het zou wellicht een verkeerde manier zijn als je op deze manier bijvoorbeeld een formulier gaat valideren. Je moet dat dan niet bot doen door gewoon een object aan te maken en dan te concluderen dat de invoer fout was als er een exception wordt gegooid.

Exceptions veroorzaken control-flow, dat is logisch. Je moet alleen uitkijken dat je exceptions ook alleen voor uitzonderlijke situaties gebruikt en niet om de control-flow standaard op deze manier te regelen.

Je kunt trouwens duplicatie van correctheids-regels voorkomen door netjes even je statische methode aan te roepen... :)

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Op zich is het een correcte manier om het af te handelen. Maar ik kan me jou twijfels heel goed voorstellen. Je kunt namelijk heel goed die waarden checken voor het aanmaken van je object. Bij IO is dat heel wat anders.

Onthoud wel goed dat een niet uitgevoerde exception handler weinig overhead veroorzaakt.

Je gebruikt hier trouwens leeftijden, het zou te overwegen kunnen zijn om hier een unsigned voor te kiezen. Dan is het vanwege je ontwerp botweg onmogelijk om er een negatieve waarde aan toe te kennen.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
The - DDD: Je gebruikt hier trouwens leeftijden, het zou te overwegen kunnen zijn om hier een unsigned voor te kiezen. Dan is het vanwege je ontwerp botweg onmogelijk om er een negatieve waarde aan toe te kennen.
* mbravenboer is hard aan het zoeken naar een unsigned int in de Java Language Specification ;) .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Het zou natuurlijk ook zo kunnen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Persoon 
{
   private int leeftijd;
 
   public Persoon(int leeftijd)
   {
     this.leeftijd = leeftijd;
   }
}

//...
if(leeftijd >= 0)
{
  Persoon persoon = new Persoon(leeftijd);
}
else
{
   //...bericht..
   //...return ofzo.
}

Zo ontwijk je dat dubbelchecken, maar om nou te zeggen dat dit een mooiere manier is, nee dat zeker niet... :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Op zaterdag 12 januari 2002 10:33 schreef minne het volgende:
Het zou natuurlijk ook zo kunnen:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Persoon 
{
   private int leeftijd;
 
   public Persoon(int leeftijd)
   {
     this.leeftijd = leeftijd;
   }
}

//...
if(leeftijd >= 0)
{
  Persoon persoon = new Persoon(leeftijd);
}
else
{
   //...bericht..
   //...return ofzo.
}

Zo ontwijk je dat dubbelchecken, maar om nou te zeggen dat dit een mooiere manier is, nee dat zeker niet... :)
Je hebt zo een unsafe object en dat is zeker geen goed ontwerp. Liever 1 keer te veel controleren dan 1 keer te weinig.

Verwijderd

Als je je class maar voor 1 doel wilt gebruiken, dan kan het wel, maar het is idd geen mooit ontwerp :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Als je alleen aan een project werkt dan is het niet zo`n probleem. Maar als je project een lange tijd duurt en meerdere mensen er aan mee werken (krijg zometeen 4 stagaires onder mijn hoede) dan wil ik toch mijn objecten veilig maken.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Wat je ook kan doen: assertions in combinatie met externe checks... In de objecten assert je de waarden en buiten de constructor hoor je de waarde te checken.

Je kunt assertions dan eventueel uit zetten later...

Maar ja eigenlijk is er helemaal geen punt: doe het gewoon grondig (was je zelf ook al van plan ;) )

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Als ik eerlijk ben begrijp de toegevoegde waarde van het assert statement niet.

Ik snap hoe ze werken, maar je zou het ook kunnen doen met een runtimeexception en door de compiler met een constante uitschakelen.
code:
1
2
3
4
5
if(debug){
   if(leeftijd<0){
     throw new IllegalArgumentException("leeftijd kan ...");
   }
}

En door een static boolean variable debug aan te maken kun je het met compileren aan en uit schakelen. Maar misschien dat ik iets gemist heb? (heb trouwens wel de beide artikels op javaworld doorgekeken).

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Alarmnummer: maar je zou het ook kunnen doen met een runtimeexception en door de compiler met een constante uitschakelen.
Klopt, maar het handige is juist dat het nu op een standaard manier kan en er ondersteuning voor is in de vm. Je moet hier expliciet een if statement schrijven. Dat is vervelend. Bovendien moet deze sowieso altijd geevalueerd worden, ook als je niet debugt.

Ik ben overigens geen groot fan van assertions hoor, ik zie het niet als een wereld-schokkende methode, maar af en toe kan het wel fraai zijn ipv zelf runtime exceptions gooien. Je moet het echter alleen gebruiken als de applicatie echt een totaal ongeldige toestand bereikt.

Design-by-contract is natuurlijk wel interessant, maar deze methode is vrij primitief en er is al gelijk een roep om een complexere, grondigere oplossing...

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • GarBaGe
  • Registratie: December 1999
  • Laatst online: 14:21
Op zaterdag 12 januari 2002 02:20 schreef mbravenboer het volgende:

[..]

* mbravenboer is hard aan het zoeken naar een unsigned int in de Java Language Specification ;) .
Volgens mij bestaat er geen "unsigned" in Java, en al zou het er wel zijn, is het absoluut niet verstandig om "unsigned" te gebruiken.

Java is namelijk gemaakt om "veilig" te zijn.

In C bijvoorbeeld, bestaat er wel unsigned en signed.
Als je dan een signed int met een unsigned int gaat vergelijken, wordt eerst de signed int automatisch gepromoot naar unsigned int. Als je signed int negatief was, is het nu ineens positief. En jij maar terugzoeken waarom er een bug optreedt bij een simpele vergelijking... >:)

Voorbeeldje (in C dus)
code:
1
2
3
4
5
6
7
8
signed int getal1 = -5;
unsigned int getal2 = 3

if ( getal1 < getal2 ) {
  printf("OK");
} else {
  printf("niet OK");
}

Hier komt dus altijd "niet OK" uit, omdat "-5" wordt gepromoot naar unsigned int, maar de binaire vorm blijft hetzelfde. int = 32-bit, dus -5 wordt: 4.294.967.291

Happy debugging >:)

(Als je de losse parameters checkt, is -5 natuurlijk wel -5, omdat er niet wordt vergeleken en dus ook niet wordt "gepromoot")

Ryzen9 5900X; 16GB DDR4-3200 ; RTX-4080S ; 7TB SSD


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Op zaterdag 12 januari 2002 14:01 schreef mbravenboer het volgende:

[..]
Bovendien moet deze sowieso altijd geevalueerd worden
if geloof dat de compiler

if(true)
ontplof()

optimaliseerd tot
ontplof();

dus er worden dan geen onnodige checks uitgevoerd, en dat gaat dus ook voor die debug. Omdat die waarde compile time al beschikbaar is.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
GarBaGe: Volgens mij bestaat er geen "unsigned" in Java
Inderdaad, de opmerking was ook grappig bedoeld ;) . The-DDD weet dat ook wel en vergiste zich gewoon even :) .
Java is namelijk gemaakt om "veilig" te zijn.
Inderdaad, maar dat is niet per definitie een reden om signed en unsigned integers niet in de taal op te nemen...
Als je signed int negatief was, is het nu ineens positief. En jij maar terugzoeken waarom er een bug optreedt bij een simpele vergelijking... >:)
Je kunt hier namelijk gewoon runtime checks gebruiken net zoals de array-bounds checking die standaard in Java zit. Hiermee zou je wellicht wel de performance winst van unsigned ints kwijtraken, dus ik betwijfel of die combinatie veel nut heeft :) .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Alarmnummer: geloof dat de compilerp
Hum ja, idd. Ik denk dat je jit dat wel doet ja, de java compiler doet het denk ik niet... Die doet niet zoveel ;) .

Het ging mij vooral om het geval if(false), maar dat is natuurlijk precies hetzelfde.

Feit is wel dat je je code moet aanpassen als je debugging uit wilt zetten, met assertions is dat niet nodig...

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Over Java en veiligheid gesproken: moet je eens naar mijn posts op deze pagina kijken (een beetje onderaan).

[topic=348173/10/25]

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment

Pagina: 1