2012-03-09 12 views
5

Mam abstrakcyjne klasy o nazwie Validator:fabryczne do tworzenia klas generycznych

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

i mam kilka konkretnych wdrożeń. Jednym z nich jest AccountValidator:

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

Innym byłoby LoginValidator:

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

teraz chcę utworzyć fabrykę do zwrotu instancji implementacji walidatora. Coś jak:

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

bym wtedy lubią robić nazwać jak

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

Jednak to nie podoba się nowy AccountCreateValidator powrotną() linię, albo fakt ja deklarując myValidator jako Validator, a nie Validator<SomeType>. Każda pomoc będzie doceniona.

+0

Kod, który masz, jest obecnie nieprawidłowy - koniec metody jest osiągalny. Pokaż krótki, ale kompletny program demonstrujący problem. –

+0

Czy nie byłoby lepiej uzyskać walidatora na podstawie typu 'IValidatable' zamiast opartego na Enum? Następnie możesz zwrócić 'Validator ' i wziąć 'T' w metodzie' GetValidator', a 'T' może być ograniczone do' IValidatable' –

+0

'Validator klasy gdzie T: IValidatable', to nie ty po prostu chcesz 'class Validator: IValidatable'? – vulkanino

Odpowiedz

2

Wygląda na to, że używasz fabryki, aby przetłumaczyć argument enum do konkretnej implementacji sprawdzania poprawności. Ale wyobrażam sobie, że chociaż osoba dzwoniąca nie zna lub nie dba o konkretny typ walidatora, prawdopodobnie zna typ, który chce zatwierdzić. To powinno oznaczać, że jest to rozsądne, aby uczynić metoda GetValidator metoda rodzajowa:

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

następnie wywołanie kodu wyglądałby następująco: Validator<IAccount> validator = ValidatorFactory.GetValidator<IAccount>(ValidationType.AccountCreate)

+0

+1. Jest to lepsze podejście, o ile wiesz, jaki typ zamierzasz zweryfikować. –

+0

Masz rację, jest wystarczająco sprawiedliwe, aby przekazać typ, który chcemy sprawdzić. Czy możesz mi pokazać, jak zwrócę instancję walidatora? Zmieniono na twój kod, ale kompilator nie jest zadowolony z mojego oświadczenia zwrotu: return new AccountCreateValidator(); – jfc37

+0

Problem polega na tym, że zwracasz walidator, który jest ważny tylko dla określonego typu TypeToValidate. Myślę, że albo chcesz zrobić tę fabrykę dla pojedynczego typu TypeToValidate, tj. Zrobić fabrykę AcountValidator, albo jeśli chcesz, aby była ona generyczna, uczyń całą fabrykę generyczną na TypeToValidate i miej mechanizm rejestrowania walidatorów betonu na wartości ValidationType . – Foo42

1

jeśli chcesz, aby był używany tak, jak powiedziałeś, bez określania ogólnego parametru, możesz zadeklarować nie ogólny interfejs i sprawić, że klasa abstrakcyjna Validator zaimplementuje to. Niesprawdzone ale coś wzdłuż tych linii:

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

następnie ten kod:

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

Powinna działać OK.

+1

Wyglądają lepiej, jednak kompilator narzeka, że ​​Validator nie implementuje IValidator.Validate (wprowadzanie obiektu) – jfc37

+0

Zaktualizowałem odpowiedź, rozwiązując ten problem, niestety, po rezygnacji z generycznych parametr zdefiniowany wszędzie, musisz sobie poradzić z tym, co się stanie, jeśli zostanie wywołany z niewłaściwym typem argumentu ... Przypuszczam, że twoje rozwiązanie będzie zależeć od tego, czy wiesz, jaki typ sprawdzania poprawności znajduje się na stronie wywołania. Jeśli tak, to rozwiązanie Foo42 jest lepsze, ponieważ zachowuje silne pisanie. Jeśli nie, to możesz pójść na kompromis z takim rozwiązaniem, jak –

0

Normalnie musiałbym fabrykę który przyjął Type jako parametr tak, aby fabryka utworzyła unikalny typ wyprowadzony. Ale ponieważ masz wiele walidatorów, które akceptują ten sam rodzaj obiektu do sprawdzenia poprawności (w tym przypadku IAccount), myślę, że będziesz musiał podać ogólny parametr fabryki, jak również rodzaj walidatora do stworzenia, na przykład:

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

próbowałem nazywając:

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

a ten zwraca się teraz o AccountCreateValidator obsady do prawidłowego Validator<IAccount> typu.

Nie jest to idealne rozwiązanie, ponieważ musisz teraz wiedzieć, jaki to poprawny weryfikator i jakie dane wejściowe akceptuje, ale powinieneś mieć nadzieję, że wystąpi błąd kompilacji w przypadku podania błędnego typu wejścia z powodu wyraźnej obsady, którą dodałem, np. ValidatorFactory.GetValidator<IUser>(ValidationType.AccountCreate) powinien się nie powieść.

EDIT: z powodu komentarza mówiąc to nie skompilować, mam edytowany powyższy fragment kodu do zrobienia do obsady jako new AccountCreateValidator() as Validator<T>. Myślałem, że może być rzucany w obie strony, ale najwyraźniej nie (nie wiem dlaczego). Sprawdziłem to w LINQPad i mogę uzyskać wynik z walidatora.

Wierzę również, że mój poprzedni komentarz na temat błędu kompilacji mógł zostać przekazany, jeśli przekazałeś nieprawidłowe typy generyczne, które nie jest już używane jako rzutowanie, używając słowa kluczowego as, po prostu zwróciliby null, gdyby nie było to możliwe.

+0

Dzięki, ale daje mi następujący błąd: Nie można przekonwertować typu "AccountCreateValidator" na "Validator " – jfc37

+0

Przepraszam za to, zredagowałem moją odpowiedź, aby naprawić to –

Powiązane problemy