Фабрика для создания универсальных классов - PullRequest
5 голосов
/ 09 марта 2012

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

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 (), или фактm объявив myValidator как Validator, а не Validator<SomeType>.Любая помощь будет оценена.

Ответы [ 3 ]

2 голосов
/ 09 марта 2012

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

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

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

1 голос
/ 09 марта 2012

Если вы хотите, чтобы он использовался, как вы сказали, без указания универсального параметра, тогда вы можете объявить неуниверсальный интерфейс и заставить ваш абстрактный класс 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); 

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

0 голосов
/ 09 марта 2012

Обычно у меня была бы фабрика, которая принимала 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) должен потерпеть неудачу.

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

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

...