2012-03-09 19 views
5

tengo una clase abstracta llamada Validador:fábrica para crear clases genéricas

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

Y tengo algunas implementaciones concretas. Una de ellas es AccountValidator:

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

Otra sería LoginValidator:

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

ahora quieren crear una fábrica para devolver la una instancia de una aplicación validador. Algo así como:

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

me gusta hacer a continuación, llamar así

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

Sin embargo, no le gusta el retorno nueva AccountCreateValidator() de la línea, o el hecho de que estoy declarando como miValidador Validador y no Validator<SomeType>. Cualquier ayuda sería apreciada.

+0

El código que tiene actualmente no es válido: se puede acceder al final del método. Por favor, muestre un programa breve pero completo que demuestre el problema. –

+0

¿No sería mejor que obtuviera un validador basado en el tipo de 'IValidatable' en lugar de basarse en un Enum? Entonces podría devolver un 'Validator ' y tomar una 'T' en el método' GetValidator' y 'T' podría estar restringido a ser' IValidatable' –

+0

'class Validator donde T: IValidatable', no es usted solo quiero 'clase Validator: IValidatable'? – vulkanino

Respuesta

2

Parece que está utilizando la fábrica para traducir un argumento enum en una implementación concreta de validación. Pero me imagino que, aunque la persona que llama no conoce o no le importa el tipo concreto del validador, presumiblemente sí conoce el tipo que desea validar. Eso debería significar que es razonable hacer que el método GetValidator un método genérico:

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

Entonces llamando código se vería así: Validator<IAccount> validator = ValidatorFactory.GetValidator<IAccount>(ValidationType.AccountCreate)

+0

+1, este es un mejor enfoque, siempre y cuando sepa de qué tipo va a validar –

+0

Tiene razón, es justo pasar en el tipo que queremos validar ¿Puedes mostrarme cómo devolvería una instancia de un validador? Cambié a su código, pero el compilador no está contento con mi declaración de devolución: return new AccountCreateValidator(); – jfc37

+0

El problema es que está devolviendo un validador que solo es válido para un tipo particular de TypeToValidate. Creo que o desea hacer esta fábrica para un solo tipo de TypeToValidate, es decir, convertirlo en una fábrica de AcountValidator, o si quiere hacerlo genérico, hacer que toda la fábrica sea genérica en TypeToValidate y tenga un mecanismo para registrar validadores concretos contra los valores de ValidationType . – Foo42

1

si quiere que se use como lo ha dicho, sin especificar el parámetro genérico, puede declarar una interfaz no genérica y hacer que la clase abstracta del Validador la implemente. No probado, pero algo en este sentido:

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(); 
     } 
    } 
} 

entonces este código:

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

debería funcionar bien.

+1

Se ve mejor, sin embargo, el compilador se queja de que Validator no implementa IValidator.Validate (entrada de objeto) – jfc37

+0

Actualicé la respuesta con una solución, lamentablemente una vez que deja de tener el genérico parámetro definido en todas partes, entonces tiene que lidiar con lo que sucede si se le llama con un tipo de argumento incorrecto ... Supongo que su solución dependerá de si sabe cuál es el tipo de validación en el sitio de la llamada. Si lo hace, entonces la solución de Foo42 es mejor, ya que conserva la tipificación fuerte. si no lo hace, entonces puede que deba comprometerse con una solución como esta –

0

Normalmente habría una fábrica que aceptó un Type como parámetro para que la fábrica cree un tipo derivado único. Pero debido a que tiene múltiples validadores que aceptan el mismo tipo de objeto para validar (en este caso, un IAccount), creo que deberá proporcionar el parámetro genérico a su fábrica, así como qué tipo de validador crear, como este:

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... 
     } 
    } 
} 

he intentado llamar:

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

y esto ahora vuelve con un reparto AccountCreateValidator a la correcta Validator<IAccount> tipo.

No es exactamente ideal ya que ahora necesita saber qué validador desea y qué entrada acepta, pero es de esperar que obtenga un error de compilación si pasa el tipo de entrada incorrecto debido a la conversión explícita que agregué, p. ValidatorFactory.GetValidator<IUser>(ValidationType.AccountCreate) debería fallar.

EDIT: debido al comentario que dice que esto no se compila, edité el fragmento de código anterior para hacer el molde como new AccountCreateValidator() as Validator<T>. Pensé que podría ser lanzado de cualquier manera, pero aparentemente no (aunque no estoy seguro de por qué). He verificado que esto funciona en LINQPad y puedo obtener un resultado de un validador.

También creo que mi comentario anterior sobre la posibilidad de obtener un error de compilación si pasa los tipos genéricos incorrectos ya no es válido para la conversión con la palabra clave as que simplemente devolvería null si no pudiera hacerlo.

+0

Gracias, pero me da el siguiente error: No se puede convertir el tipo 'AccountCreateValidator' a 'Validator ' – jfc37

+0

Disculpa, he editado mi respuesta para rectificar esto –

Cuestiones relacionadas