user7964141
user7964141

Reputation:

Design Patterns for dependent sequential validation

I have three validators each sequentially dependent on the previous. I might use all three or only the first two or only the first one. E.g.

  1. Validator 1: Checks whether user exists
  2. Validator 2: Checks whether requested order exists in the user's order history
  3. Validator 3: Checks whether that order has a cancel option

Each validator in this list will throw an error if the previous condition is not met. I'm currently using this pattern:

function validator1() { ... }

function validator2() { validator1(); ... }

function validator3() { validator2(); ... }

But this feels like an anti-pattern to me because all of the prerequisite validation is done implicitly so when I run validtor2(), it is not clear that validator1() is also being run.

I also considered doing this:

function validator1() { ... }

function validator2() { ... }

function validator3() { ... }

function someTask() { validator1(); validator2(); validator3(); ... }

But this is unsafe because there is nothing stopping me from running validator3() and throwing an error if condition 2 hasn't been met.

Is there a way to do this that is both safe and explicit?

Edit

This is a great reference for the design pattern mentioned in the accepted answer: https://refactoring.guru/design-patterns/chain-of-responsibility

Upvotes: 3

Views: 10778

Answers (1)

Andreas Hütter
Andreas Hütter

Reputation: 3919

I suggest looking into the Chain of Responsibility and the Builder Pattern specific for validation. See here:

You could also look into usage of the Decorator Pattern:

Is there a way to do this that is both safe and explicit?

But before considering one of the patterns which might of course provide more flexibility but also more complexity consider if your rules might really get extended a lot and will be reused and that many cases. Otherwise it might be a bit of over engineering to go with one of the suggested patterns right away.

Also, you can make a lot explizit by using meaningful function names that explicitly tell the reader of the code what specific use case is being performed. Such a function than acts more like an orchestrator of the required validation steps. So your approach (despite of the bad name someTask()) might already fit your needs.

But this is unsafe because there is nothing stopping me from running validator3() and throwing an error if condition 2 hasn't been met.

You could either have each validate exception throw an exception or introduce guard clauses having the major task return if one of the validations fail.

Another option would be to pass in or register a list of validations with the same interface and loop them through. If you need to check for all validations and collect the results (e.g. error code or messages) for each validation step or throw an exception on the first validation is up to your needs.

Upvotes: 6

Related Questions