2012-03-09 25 views
5

ho una classe astratta denominata Validator:di sviluppo per creare classi generiche

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

E ho alcune implementazioni concrete. Uno è AccountValidator:

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

Un'altra potrebbe essere LoginValidator:

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

Ora voglio creare una fabbrica per restituire l'un'istanza di un'implementazione validatore. Qualcosa di simile:

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

Mi piacerebbe poi piace fare chiamarlo come

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

Tuttavia non piace il ritorno nuova AccountCreateValidator() la linea, o il fatto che sto dichiarando MyValidator come Validatore e non Validator<SomeType>. Qualsiasi aiuto sarebbe apprezzato.

+0

Il codice che hai è attualmente non valido - la fine del metodo è raggiungibile. Si prega di mostrare un programma breve ma completo che mostri il problema. –

+0

Non sarebbe meglio ottenere un validatore basato sul tipo di IValidatable piuttosto che basato su un Enum? Quindi è possibile restituire un 'Validator ' e prendere un 'T' nel metodo' GetValidator' e 'T' potrebbe essere limitato ad essere di' IValidatable' –

+0

'Validator dove T: IValidatable', non è quello che si vuoi solo 'class Validator: IValidatable'? – vulkanino

risposta

2

Sembra che si stia utilizzando la fabbrica per tradurre un argomento enum in un'implementazione di convalida concreta. Ma immagino che sebbene il chiamante non sappia o si preoccupi del tipo concreto del validatore, presumibilmente conosce il tipo che desidera convalidare. Questo dovrebbe significare che è ragionevole per rendere il metodo getValidator un metodo generico:

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

quindi chiamando il codice sarebbe simile a questa: Validator<IAccount> validator = ValidatorFactory.GetValidator<IAccount>(ValidationType.AccountCreate)

+0

+1 questo è un approccio migliore, purché tu sappia che tipo stai per convalidare –

+0

Hai ragione, è giusto passare tipo che vogliamo convalidare. Puoi per favore mostrarmi come restituire un'istanza di un validatore? Cambiato nel codice ma il compilatore non è soddisfatto della mia dichiarazione di reso: return new AccountCreateValidator(); – jfc37

+0

Il problema è che si sta restituendo un validatore che è valido solo per un particolare tipo di TypeToValidate. Penso che vuoi rendere questo factory per un singolo tipo di TypeToValidate, cioè renderlo un factory AcountValidator, o se vuoi renderlo generico, rendere l'intero factory generico su TypeToValidate e avere un meccanismo per registrare validatori concreti contro valori ValidationType . – Foo42

1

se si desidera che venga utilizzato come si è detto, senza specificare il parametro generico, è possibile dichiarare un'interfaccia non generica e far sì che la classe astratta Validator lo implementa. qualcosa testato ma in queste righe:

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

poi questo codice:

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

dovrebbe funzionare bene.

+1

Guardando meglio, tuttavia il compilatore si lamenta che Validator non implementa IValidator.Validate (input oggetto) – jfc37

+0

Ho aggiornato la risposta con una soluzione a questo, purtroppo una volta che hai rinunciato ad avere il generico parametro definito dappertutto, quindi devi fare i conti con ciò che accade se ti chiamano con il tipo di argomento sbagliato ... Suppongo che la tua soluzione dipenda da se sai qual è il tipo di convalida nel sito di chiamata. Se lo fai, la soluzione di Foo42 è migliore in quanto mantiene la forte digitazione. se non lo fai, potresti dover scendere a compromessi con una soluzione come questa –

0

Normalmente avrebbe una fabbrica che ha accettato un Type come parametro in modo che la fabbrica crei un tipo derivato unico. Ma poiché hai più validatori che accettano lo stesso tipo di oggetto da convalidare (in questo caso uno IAccount) penso che dovrai fornire il parametro generico alla tua fabbrica e il tipo di validatore da creare, come questo:

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

ho provato a chiamare:

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

e questo ora restituisce un AccountCreateValidator getto alla corretta Validator<IAccount> tipo.

Non è esattamente l'ideale come ora necessario sapere che cosa validatore si vuole e ciò di input accetta, ma si spera, dovrebbe ottenere un errore di compilazione se passando il tipo di ingresso sbagliata a causa del cast esplicito ho aggiunto, per esempio ValidatorFactory.GetValidator<IUser>(ValidationType.AccountCreate) dovrebbe fallire.

EDIT: a causa del commento che dice questo non sarebbe compilare, Ho modificato il frammento di codice qui sopra per fare il cast come new AccountCreateValidator() as Validator<T>. Ho pensato che potesse essere lanciato in entrambi i modi, ma apparentemente non (non sono sicuro del perché). Ho verificato che funzioni in LINQPad e posso ottenere un risultato da un validatore.

Credo anche il mio commento precedente riguardo alla possibilità di ottenere un errore di compilazione se passate nei tipi generici sbagliate non si pone come la fusione con la parola as sarebbe solo tornare null se era in grado di farlo.

+0

Grazie, ma mi dà il seguente errore: Impossibile convertire il tipo 'AccountCreateValidator' in 'Validator ' – jfc37

+0

Mi dispiace, ho modificato la mia risposta per correggere questo –

Problemi correlati