2012-03-09 5 views
5

У меня есть абстрактный класс Validator:Factory создать общие классы

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

И у меня есть несколько конкретных реализаций. Одним из них является AccountValidator:

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

Еще бы LoginValidator:

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

Теперь я хочу, чтобы создать фабрику, чтобы вернуть экземпляр реализации валидатора. Что-то вроде:

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

я тогда хотел бы сделать называть это как

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

Однако это не нравится возвращение нового AccountCreateValidator() линию, или тот факт, я объявляю myValidator, как Валидатор, а не Validator<SomeType>. Любая помощь будет оценена по достоинству.

+0

Код, который у вас есть, в настоящее время недействителен - конец метода доступен. Пожалуйста, покажите короткую, но полную программу, которая демонстрирует проблему. –

+0

Не могли бы вы лучше получить валидатор, основанный на типе 'IValidatable', а не на основе Enum? Затем вы можете вернуть 'Validator ' и взять' T' в методе 'GetValidator', а' T' может быть ограничено значением 'IValidatable' –

+0

' class Validator , где T: IValidatable', это не значит, что вы просто нужно 'class Validator: IValidatable'? – vulkanino

ответ

2

Кажется, что вы используете фабрику для преобразования аргумента enum в конкретную реализацию проверки. Но я бы предположил, что, хотя вызывающий не знает или не заботится о конкретном типе валидатора, он, по-видимому, знает тип, который он хочет проверить. Это должно означать, что разумно сделать метод GetValidator общий метод:

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

Затем вызывающий код будет выглядеть следующим образом: Validator<IAccount> validator = ValidatorFactory.GetValidator<IAccount>(ValidationType.AccountCreate)

+0

+1 это лучший подход, если вы знаете, какой тип вы собираетесь проверять –

+0

Вы правы, это достаточно справедливо, чтобы пройти в тип, который мы хотим проверить. Не могли бы вы показать мне, как я верну экземпляр валидатора? Изменен для вашего кода, но компилятор недоволен моим оператором return: return new AccountCreateValidator(); – jfc37

+0

Проблема в том, что вы возвращаете валидатор, который действителен только для определенного типа TypeToValidate. Я думаю, вы либо хотите сделать эту фабрику для одного типа TypeToValidate, т. Е. Сделать ее фабрикой AcountValidator, или если вы хотите сделать ее общей, сделать весь заводской тип на TypeToValidate и создать механизм для регистрации конкретных валидаторов против значений ValidationType , – Foo42

1

Если вы хотите, чтобы он использовался, как вы сказали, без указания общего параметра, вы можете объявить не общий интерфейс и сделать его абстрактным классом Validator. Непроверенные но что-то вдоль этих линий:

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

этот код:

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

Должно работать нормально.

+1

. Посмотрите лучше, однако компилятор жалуется, что Validator не реализует IValidator.Validate (ввод объекта) – jfc37

+0

Я обновил ответ с помощью решения этого, к сожалению, как только вы откажитесь от родового параметр, определенный везде, тогда вам нужно иметь дело с тем, что происходит, если вы вызываетесь с неправильным типом аргумента ... Я полагаю, ваше решение будет зависеть от того, знаете ли вы, что тип проверки находится на сайте вызова. Если вы это сделаете, то решение Foo42 лучше, так как оно сохраняет сильную типизацию. если вы этого не сделаете, вам, возможно, придется пойти на компромисс с таким решением, как это –

0

Обычно я бы иметь завод, который принял Type как чтобы фабрика создавала уникальный производный тип. Но поскольку у вас есть несколько валидаторов, которые принимают один и тот же объект для проверки (в этом случае IAccount), я думаю, вам нужно будет предоставить общий параметр на вашем заводе, а также какой тип валидатора для создания, например:

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

Я попытался назвать:

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

и это теперь возвращает AccountCreateValidator приведение к правильному Validator<IAccount> типа.

Это не идеальный вариант, так как теперь вам нужно знать, какой валидатор вы хотите, и какой ввод он принимает, но вы должны, надеюсь, получить ошибку компиляции, если передаете неправильный тип ввода из-за явного добавления я добавил, например. ValidatorFactory.GetValidator<IUser>(ValidationType.AccountCreate) не работает.

EDIT: Из-за комментария, говорящего, что это не скомпилируется, я отредактировал приведенный выше фрагмент кода, чтобы сделать литье как new AccountCreateValidator() as Validator<T>. Я думал, что это может быть брошено в любом случае, но, видимо, нет (не знаю, почему, хотя). Я подтвердил, что это работает в LINQPad, и я могу получить результат от валидатора.

Я также считаю, что мой предыдущий комментарий о возможной ошибке компиляции, если вы передаете неправильные общие типы, больше не стоит, поскольку литье с использованием ключевого слова as просто вернет null, если он не смог это сделать.

+0

Спасибо, но он дает мне следующую ошибку: Невозможно преобразовать тип 'AccountCreateValidator' в 'Validator ' – jfc37

+0

Простите, отредактировал мой ответ, чтобы исправить это –

Смежные вопросы