Уровень проверки на основе правил - PullRequest
1 голос
/ 19 июня 2019

Я пытаюсь реализовать этот шаблон .

Начиная с абонента, я хотел бы сделать что-то вроде этого:

var validatore = new Validator();

validatore.AddRule<TestRule>("OK");
validatore.AddRule<int>(45);

validatore.Validate();

Выполнение правил:

public interface IValidationRule<T>
{
    string Error { get; set; }
    bool Validate(T arg);
}

public class TestRule : IValidationRule<string>
{
    public string Error { get; set; }
    public bool Validate(string arg)
    {
        return arg == "test";
    }
}

Проблема заключается в конкретной реализации валидатора. Я предположил что-то подобное:

public interface IValidator
{
    void AddRule<TRule>(dynamic arg);
    ValidationResult Validate();
}

public class Validator : IValidator
{
    public void AddRule<T>(dynamic arg)
    {
        ???
    }

    public ValidationResult Validate()
    {
        forEach ...
    }
}

Где я должен поместить каждое универсальное правило в один объект коллекции (AddRule)? Правильно ли реализована моя подобная реализация?

1 Ответ

0 голосов
/ 19 июня 2019

Этот паттерн слишком абстрактен для начала. В том смысле, что вы получите более полезные обобщения, если начнете с того, что именно вы хотите сделать, а затем сделаете абстракции поверх этого. Мол, в этом случае не очевидно, что проверяется? Пользовательский ввод в некоторый интерфейс? Или данные из какого-то xml? В первом случае вам потребуется не только сообщение об ошибке, но, скорее всего, также какое-либо действие, которое обновляет интерфейс в соответствии с вашей ошибкой. И тогда абстракция валидации, скорее всего, примет совсем другую форму. И откуда будет исходный код, который выполняет проверку? Необходимость реализации специальных классов для каждого правила валидации создаст потрясающую абстракцию, но излишне сложный код, когда вы действительно захотите его использовать.

Кроме того, я бы использовал динамический тип только тогда, когда совсем нет другого пути.

Итак, если вы все еще заинтересованы, приведенный ниже код будет работать с проверкой ссылочных типов. Опять же, если вам нужна система валидации, типы значений, которые вам понадобятся для валидации, обычно являются свойствами некоторого класса, но все же полезно иметь в виду, что он не универсален.

public class ValidationResult
{
    public bool HasError { get; set; } = false;
    public string ErrorText { get; set; } = "No error";
    public void SetError(string errorMsg)
    {
        this.HasError = true;
        ErrorText = errorMsg;
    }
}

public interface IValidationRule
{
    ValidationResult Validate();
}

public class ValidationRule<T> : IValidationRule
{
    private Func<T, ValidationResult> validatorFunc;
    private T validationSubject;
    public ValidationRule(T validationSubject, Func<T, ValidationResult> validatorFunc)
    {
        this.validationSubject = validationSubject;
        this.validatorFunc = validatorFunc;
    }

    public ValidationResult Validate() => validatorFunc?.Invoke(validationSubject);
}

public interface IValidator
{
    void AddRule<T>(T validationSubject, Func<T, ValidationResult> validationFunc);
    ValidationResult Validate();
}

public class Validator : IValidator
{
    private readonly List<IValidationRule> rules = new List<IValidationRule>();

    public void AddRule<T>(T validationSubject, Func<T,ValidationResult> validationFunc)
    {
        rules.Add(new ValidationRule<T>(validationSubject, validationFunc));
    }

    public ValidationResult Validate()
    {
        foreach (var rule in rules)
        {
            var result = rule.Validate();
            if (result.HasError) return result;
        }

        return new ValidationResult();
    }
}

Чтобы добавить правило проверки, вы передаете два параметра: объект, который должен быть проверен через некоторое время, и Func, который содержит логику и возвращает ValidationResult. Как это:

public class ValidatableObject
{
    public int intValue;
}

private void Test()
{
    var target = new ValidatableObject();
    target.intValue = 1;

    var validator = new Validator();
    validator.AddRule(target, (x) =>
    {
        var validationResult = new ValidationResult();
        if (x.intValue > 10) validationResult.SetError("Exceeds max value (10)");
        return validationResult;
    });

    log(validator.Validate().ErrorText);
    target.intValie = 100;
    log(validator.Validate().ErrorText);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...