Question

I have an abstract class called Validator:

public abstract class Validator<T> where T : IValidatable
{
    public abstract bool Validate(T input);
}

And I have a few concrete implementations. One is AccountValidator:

public class AccountCreateValidator : Validator<IAccount>
{
    public override bool Validate(IAccount input)
    {
        //some validation
    }
}

Another would be LoginValidator:

public class LoginValidator : Validator<IAccount>
{
    public override bool Validate(IAccount input)
    {
        //some different validation
    }
}

I now want to create a factory to return the an instance of a validator implementation. Something like:

public static class ValidatorFactory
{
    public static Validator GetValidator(ValidationType validationType)
    {
        switch (validationType)
        {
            case ValidationType.AccountCreate:
                return new AccountCreateValidator();
        }
    }
}

I'd then like to do call it like

Validator myValidator = ValidatorFactory.GetValidator(ValidationType.AccountCreate); 

However it doesn't like the return new AccountCreateValidator() line, or the fact I'm declaring myValidator as Validator and not Validator<SomeType>. Any help would be appreciated.

Was it helpful?

Solution

It seems that you are using the factory to translate an enum argument into a concrete validation implementation. But I would imagine that although the caller does not know or care about the concrete type of the validator, it presumably does know the type it wishes it to validate. That should mean that it is reasonable to make the GetValidator method a generic method:

public static Validator<TypeToValidate> GetValidator<TypeToValidate>(ValidationType validationType) where TypeToValidate : IValidatable

Then calling code would look like this: Validator<IAccount> validator = ValidatorFactory.GetValidator<IAccount>(ValidationType.AccountCreate)

OTHER TIPS

if you want it to be used like you have said, without specifying the generic parameter then you can declare a non generic interface and make you Validator abstract class implement that. Untested but something along these lines:

public interface IValidator 
{
    bool Validate(object input);
}

public abstract class Validator<T> : IValidator where T : IValidatable
{
    public abstract bool Validate(T input);

    public bool Validate (object input)
    {
        return Validate ((T) input);
    }
}

public static class ValidatorFactory
{
    public static IValidator GetValidator(ValidationType validationType)
    {
        switch (validationType)
        {
            case ValidationType.AccountCreate:
                return new AccountCreateValidator();
        }
    }
}

then this code:

IValidator myValidator = ValidatorFactory.GetValidator(ValidationType.AccountCreate); 

Should work ok.

Normally I would have a factory which accepted a Type as a parameter so that the factory would create a unique derived type. But because you have multiple validators that accept the same kind of object to validate (in this case an IAccount) I think you'll have to provide the generic parameter to your factory as well as what kind of validator to create, like this:

public static class ValidatorFactory
{
    public static Validator<T> GetValidator<T>(ValidationType validationType) 
        where T : IValidatable
    {
        switch (validationType)
        {
            case ValidationType.AccountCreate:
                return new AccountCreateValidator() as Validator<T>;
                    // etc...
        }
    }
}

I've tried calling:

var value = ValidatorFactory.GetValidator<IAccount>(ValidationType.AccountCreate)

and this now returns an AccountCreateValidator cast to the correct Validator<IAccount> type.

It's not exactly ideal as you now need to know what validator you want and what input it accepts, but you should hopefully get a compilation error if passing the wrong input type because of the explicit cast I've added, e.g. ValidatorFactory.GetValidator<IUser>(ValidationType.AccountCreate) should fail.

EDIT: because of the comment saying this would not compile, I've edited the above code snippet to do the cast as new AccountCreateValidator() as Validator<T>. I thought it could be cast either way, but apparently not (not sure why though). I have verified this works in LINQPad and I can get a result back from a validator.

I also believe my previous comment about possibly getting a compilation error if you pass in the wrong generic types no longer stands as casting using the as keyword would just return null if it was unable to do it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top