I used a combination of both solutions: for each validation function, I pass a record that I fill with the validation status (an error code). At the end of the function, if a validation error exists, I throw an exception, this way I do not throw an exception for each field, but only once.
I also took advantage that throwing an exception will stop execution because I do not want the execution to continue when data is invalid.
For example
procedure Validate(var R:TValidationRecord); begin if Field1 is not valid then begin R.Field1ErrorCode=SomeErrorCode; ErrorFlag := True; end; if Field2 is not valid then begin R.Field2ErrorCode=SomeErrorCode; ErrorFlag := True; end; if Field3 is not valid then begin R.Field3ErrorCode=SomeErrorCode; ErrorFlag := True; end; if ErrorFlag then ThrowException end; If relying on boolean only, the developer using my function should take this into account writing:
if not Validate() then DoNotContinue(); but he may forgot and only call Validate() (I know that he should not, but maybe he might).
So, in the code above I gained the two advantages:
- Only one exception in the validation function.
- Exception, even uncaught, will stop the execution, and appear at test time