Как мне разработать классы проверки строки в C #? - PullRequest
3 голосов
/ 08 ноября 2011

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

Я не лучший, когда дело доходит до ОО дизайна. Что мне нужно, так это возможность для пользователя ввести произвольную строку, а затем для библиотеки валидации проверить ее на соответствие известным типам, чтобы убедиться, что она соответствует одному из них. Должен ли я создать интерфейс и сделать каждый тип строки классом, который реализует этот интерфейс? (кажется неправильным, так как я не буду знать тип, когда читаю в строке). Я мог бы использовать некоторую помощь в определении шаблона для этого.

Спасибо.

Ответы [ 3 ]

3 голосов
/ 08 ноября 2011

Я всегда был фанатом Свободной проверки для .Net. Если он более надежный, чем вам нужен, его функциональность достаточно проста, чтобы имитировать самостоятельно.

Если выВы заинтересованы, вот ссылка на мой очень простой класс проверки .Он похож на использование для Fluent Validation, но использует лямбда-выражения для создания проверочных утверждений.Вот краткий пример того, как его использовать:

public class Person
{
    public Person(int age){ Age = age; }
    public int Age{ get; set;}
}

public class PersonValidator : AbstractValidator
{
    public PersonValidator()
    {
        RuleFor(p => p.Age >= 0,
            () => new ArgumentOutOfRangeException(
                "Age must be greater than or equal to zero."
        ));
    }
}

public class Example
{
    void exampleUsage()
    {
        var john = new Person(28);
        var jane = new Person(-29);

        var personValidator = new PersonValidator();

        var johnsResult = personValidator.Validate(john);
        var janesResult = personValidator.Validate(jane);

        displayResult(johnsResult);
        displayResult(janesResult);
    }

    void displayResult(ValidationResult result)
    {
        if(!result.IsValid)
            Console.WriteLine("Is valid");
        else
            Console.WriteLine(result.Exception.GetType());
    }
}

(более подробный пример см. исходный код ).

Вывод:

Is valid
System.ArgumentOutOfRangeException
2 голосов
/ 08 ноября 2011

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

Ваша проблема похожа нанастраиваемые ограничения NUnit.

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

Использование объектной модели ограничений превосходит чисто функциональную модель ограничений:

  • Она позволяет объединять подусловия для оценкиограничение более высокого уровня.
  • Позволяет предоставить диагностическую информацию о том, почему конкретное ограничение не соответствует вашим входным данным.

Звучит странно, но ограничения - это просто функции, которые принимаютпараметр желаемого типа возвращает true, если он совпадает, и false, если он не соответствует.

Адаптация к вашей проблеме

Мне нужна возможность ввести произвольную строку, а затем проверочную библиотеку проверить ее на соответствие известнымтипы, чтобы убедиться, что он соответствует одному из них.

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

Но я не рекомендую вам выполнять автоматическую классификацию.Я рекомендую вам прикрепить конкретное ограничение к конкретному входу, а не пытаться сопоставить все доступные ограничения.Передайте string этому ограничению и назовите его готовым.

Если вам нужно сделать это для объектов более высокого уровня, создайте ограничение для объекта более высокого уровня, который использует определенные (существующие) ограничения для каждого изего подполя, а также выполнение проверки ограничений между полями.

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

Кстати, я бы не использовал тот же интерфейс, который использует NUnit:

  • Это сбивающий с толку дизайн
  • Я бы предпочел подход, использующий все дженерикичерез
  • Я бы предпочел подход, который позволял бы вам возвращать IEnumerable<ConstraintViolation> или IEnumerable<string>, а не принимать какой-либо класс выходного писателя в качестве зависимости

Но я бы определенно украл базовую концепцию:)

Реализация

Вот пример реализации того, о чем я говорю:

public class ConstraintViolation
{
    public ConstraintViolation(IConstraintBase source, string description)
    {
        Source = source;
        Description = description;
    }

    public IConstraintBase Source { get; }
    public string Description { get; set; }
}

public interface IConstraintBase
{
    public string Name { get; }
    public string Description { get; }
}

public interface IConstraint<T> : IConstraintBase
{
    public IEnumerable<ConstraintViolation> GetViolations(T value);
}

И здесьВот пример ограничения для проверки длины строки (слабый пример, но посмотрите мои комментарии по этому поводу ниже):

public class StringLengthConstraint : IConstraint<string>
{
    public StringLengthConstraint(int maximumLength)
        : this(minimumLength: 0, maximumLength: maximumLength)
    {
    }

    public StringLengthConstraint(int minimumLength, int maximumLength,
        bool isNullAllowed = false)
    {
        MinimumLength = minimumLength;
        MaximumLength = maximumLength;
        IsNullAllowed = isNullAllowed;
    }

    public int MinimumLength { get; private set; }
    public int MaximumLength { get; private set; }
    public bool IsNullAllowed { get; private set; }

    public IEnumerable<ConstraintViolation> GetViolations(string value)
    {
        if (value == null)
        {
            if (!IsNullAllowed)
            {
                yield return CreateViolation("Value cannot be null");
            }
        }
        else
        {
            int length = value.Length;

            if (length < MinimumLength)
            {
                yield return CreateViolation(
                    "Value is shorter than minimum length {0}",
                    MinimumLength);
            }

            if (length > MaximumLength)
            {
                yield return CreateViolation("Value is longer than maximum length {0}",
                    MaximumLength);
            }
        }
    }

    public string Name
    {
        get { return "String Length"; }
    }

    public string Description
    {
        get
        {
            return string.Format("Ensure a string is an acceptable length"
                + " - Minimum: {0}"
                + ", Maximum: {1}"
                + "{2}"
                , MinimumLength
                , MaximumLength
                , IsNullAllowed ? "" : ", and is not null"
                );
        }
    }

    private ConstraintViolation CreateViolation(string description,
        params object[] args)
    {
        return new ConstraintViolation(this, string.Format(description, args));
    }
}

Вот как его использовать при проверке одного поля:

var violations = new StringLengthConstraint(10).GetViolations(value);

if(violations.Any())
{
    throw new InvalidArgumentException("value", string.Join(", ", violations));
}

Обоснование

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

Его можно использовать повторно

Напишите это или используйте его один раз, и я бы согласился, что это боль.

Но большая часть кода здесь предназначена для возможности многократного использования.Например, вы можете выбрать это из списка ограничений для типа string.Или вы можете отобразить список ограничений или нарушений ограничений в пользовательском интерфейсе с подсказками и т. Д. Или вы можете использовать его в рамках модульного тестирования;С помощью класса адаптера он может подключаться непосредственно к NUnit.

Эта модель поддерживает агрегирующие ограничения и нарушения

  • Через Linq
  • Через композицию объекта

Linq:

var violations = new SomeConstraint(someData).GetViolations(value)
    .Concat(new SomeOtherConstraint(someData).GetViolations(value))
    ;

Состав объекта:

// ...
public IEnumerable<ConstraintViolation> GetViolations(SomeType value)
{
    if(value == 42)
    {
        yield return new ConstraintViolation(this, "Value cannot be 42");
    }

    foreach(var subViolation in subConstraint.GetViolations(value))
    {
        yield return subViolation;
    }
}

private SomeSubConstraint subConstraint;
0 голосов
/ 08 ноября 2011

Вам необходимо сделать следующее:

  1. Разберите вашу строку и выясните (каким-то образом), какой это тип строки. Я бы предпочел знать это до проверки (назначая типы полям), потому что если какая-то строка неверна, вы можете назначить ей неправильный тип.
  2. Проверьте вашу строку на основе правил проверки, применимых к данному типу поля. Эти валидаторы должны реализовывать некоторый интерфейс, чтобы вы могли проверять любой тип строки. Обычно у вас есть не только валидация по типу поля, но и валидация по полю, поэтому этот вид валидаторов также должен реализовывать тот же интерфейс.

Все остальное зависит от логики вашего приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...