views:

115

answers:

4

I have the following method that takes in a details object, validates it, converts it to a request and enqueues it. Everything is fine apart from the validate request which I am having trouble with. Basically, there is different validation logic for each different details object. I know from the generic constraint that the details object must have a base class of BaseDetails and from the actual generic parameter I know the exact derived type, but do not know how to use these to write my validator class so it handles all types of details:

private void Enqueue<TDetails, TRequest>(TDetails details)  
   where TDetails: BaseDetails where TRequest: BaseRequest
{
  bool isValid = _validator.Validate(details);

  if (isValid)
  {
    TRequest request = ObjectMapper
      .CreateMappedMessage<TDetails, TRequest>(details);

    _queue.Enqueue(request);
  }
}
+2  A: 

It just means that there must be a corresponding hierarchy of validators, each attached to its own TDetails object. Since TDetails is always BaseDetails, you need to manually specify which child class you support and chain execution to a nested validator.

Kerido
A: 

I think you need to create a validator class for each implementation of TDetails which knows how to validate that particular implementation, then have a factory to produce the right validator for a given TDetails implementation and have your _validator get the correct class for doing the work from the factory and get the class to do the validation.

Obviously you could have some of the common validation in a base class.

You might be better having the validation on the object itself though rather than create a separate validator for each TDetails implementation...

Sam Holder
Re: the factory - how would this be defined? Each validator would have a different method signature as they would be taking in a different type, or would I declare them all to take in the base class and then cast to the derived type in each validation method?
Stacey Moore
the factory would have a method `IDetailsValidator GetValidator(TDetails)` and would then do a `switch(typeof(TDetails))` and then for each case return an implementation of that interface. this interface would have a method `bool IsValid(TDetails)`. Each implementation would cast to the correct type in the method and do the validation.
Sam Holder
you can't get away from the big switch (or if then else) construct, so it is a good idea to have it in a single place (the factory class) where you can go to add new validators as and when they are written, rather than having to do the check in several places in your code.
Sam Holder
+1  A: 

For me it seems that validation logic should be attached to details objects themselves (if possible, of course). Then you could mark base class abstract and override Validate method for specific details classes if necessary.

On the other hand - 'composition over inheritance' is trendy at the moment.

Arnis L.
That would be easiest but TDetails are WCF data contracts and I am not allowed to touch them!
Stacey Moore
then it looks like you are stuck with one validator per TDetails. Lets just hope that you have control of the TDetails implementations...
Sam Holder
@Stacey Then i would use dependency injection container to pass in necessary validator (base or derived details class specific). Similarly as in widely accepted Repository pattern.
Arnis L.
+1  A: 

I know from the generic constraint that the details object must have a base class of BaseDetails

This is known in process of compilation to byte code (i mean Visual Studio knows it)

and from the actual generic parameter I know the exact derived type

But this is known only after JIT compilation (Visual Studio doesn't know anything about it). It's like late binding.

So if you want to write one validator class with several methods with different argument types, you can't do this, because Visual Studio compiler didn't know (in compile time) wich method will be called.

I believe that there are no ways to skip writing 'switch(typeof(TDetails))' logic, where validator must be selected by TDetails. So you must write some kind of factory, like Sam Holder wrote above.

PS: Sorry for my english. I'm using stackoverflow also for study english writing :)

SpeCT