Consider the following approach to validation of an API class (POJO or what have you, I mean a class which just acts as a container for some properties), we make all constructors private to the API scope (and actually let us insist there only be one constructor), and the constructor does validation of its inputs, throwing exceptions if the inputs are invalid.
We then provide some static method either in the API package or on the class itself (and then make the constructor fully private). All this static method does is call the constructor for us and transform the exception case into something nicer. I'm mostly a scala developer so let's say we get a
Either[APIDefinedExceptionTrait, ClassWeWant]
Now the only way to make a ClassWeWant is through this static method.
If I contrast this with the typical approach I see (in Scala), of instantiating the class and then having an external validation method (I really dislike this approach), I see a couple of important to me benefits.
- It is not possible to instantiate a class with invalid state and avoid the validation.
- The validation, as it lives directly in the class, is essentially self documenting the constraints the properties of the class has. With the external method approach, theis is decoupled and it can be hard to know what constraints hold.
- It makes, to me, more logical sense. If my type system was powerful enough to encode the constraints in my type without it being tediously complex, I would do that and then the constraint would live in the definition of the class itself (which is what my proposed approach to validation does).
I have never seen this approach in code, so I suspect it's a bad idea. But I can't really understand why, I see two downsides and one suspcious thing.
Downside 1 - It becomes tedious to create the class when you know the inputs are already correct. But I mostly see this validation pattern used in relation to user input, e.g. serialising a post body into a class where the class is only really liable to be made once and then passed around (and admittedly maybe serialised/deserialized).
Downside 2 - It has the overhead of creating exceptions, handling them etc. But as above for my use case I feel this is not a problem as my classes will be small and infrequently made.
Suspcious thing - I am using exceptions for control flow, but I think I am justified as a work around of how constructors work. If I could tell the constructor to return an Either of course I of course would.
Are there some more obvious problems with this?