1

检查输入的最有效方法是什么?

  1. 如何防止嵌套和臃肿的 if 语句?
  2. 使用异常是正确的做法吗?如果不是,我应该遵循什么样的方法?

不好的例子(我认为):

public int doSthWithAge(int age)
{
    if (age > 0)
    {
        if (age > 100)
        {
            throw new AgeIsTooHighException();
        }
    }
    else
    {
        throw new NoWayException();
    }
...
}

但是有什么好的方法呢?

(如果您要提供任何特定于语言的信息,请像我在 C# -syntax-wise- 中进行验证一样进行操作)

4

2 回答 2

5

一种常见的面向对象的验证技术是将验证规则建模为一等对象:

  1. 定义用于验证特定类型数据的通用接口
  2. 实现符合该接口的类或函数的集合
  3. 循环遍历此集合中的每个函数/对象并调用验证方法。返回值是真/假,或者可能是描述验证失败的对象(如果验证规则通过,则返回 null)。在迭代规则集合时构建验证失败列表
  4. 以适当的方式向用户呈现验证失败

您会看到许多图书馆都在使用这种技术。

例子:

// the entity you want to validate
public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }
}

public class ValidationFailure
{
    public ValidationFailure(string description) { Description = description; }
    public string Description { get; set; }
    // perhaps add other properties here if desired
}

// note that this is generic and can be reused for any object to validate
public interface IValidationRule<TEntity>
{
    ValidationFailure Test(TEntity entity);
}

public class ValidatesMaxAge : IValidationRule<Person>
{
    public ValidationFailure Test(Person entity)
    {
        if (entity.Age > 100) return new ValidationFailure("Age is too high.");
    }
}

public class ValidatesName : IValidationRule<Person>
{
    public ValidationFailure Test(Person entity)
    {
        if (string.IsNullOrWhiteSpace(entity.Name))
            return new ValidationFailure("Name is required.");
    }
}

// to perform your validation
var rules = new List<IValidationRule> { new ValidatesMaxAge(), new ValidatesName() };
// test each validation rule and collect a list of failures
var failures = rules.Select(rule => rule.Test(person))
    .Where(failure => failure != null);
bool isValid = !failures.Any();

这种设计的优点:

  • 符合接口将促进代码模式的一致性
  • 每个验证规则一个类或一个函数使您的规则保持原子性、可读性、自记录性、可重用性
  • 遵循验证规则类的单一职责原则,并有助于简化需要执行验证的代码
  • 降低圈复杂度(更少的嵌套if语句),因为它是一种更面向对象或功能性的方法,而不是程序性方法
  • 允许为单个验证规则类引入依赖注入,这在您访问数据库或服务类进行验证时很有用

编辑:@Mgetz 提到了输入验证与业务验证,这也是一个重要的考虑因素。上述按规则分类的方法基于我每天使用的系统。我们更多地将其用于服务类中的业务逻辑验证(这是一个具有许多业务规则的复杂企业系统),并且该设计很好地服务于我们的目的。我上面写的具体规则非常简单,看起来更像是输入验证。对于输入验证,我建议使用更轻量级的方法,并在适当时将其与业务逻辑验证分开。

这种设计的另一种实现是每个实体有一个验证器类,并使用更轻量级的东西,例如 lambdas 用于单独的验证规则。例如,流行的Fluent Validation库使用它。这对于用户输入验证非常有用,因为它允许执行简单验证的代码更少,并鼓励您将输入验证与业务逻辑验证分开:

// Example using the FluentValidation library
public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(p => p.Age).LessThan(100);
        RuleFor(p => p.Name).NotEmpty();
    }
}
于 2013-07-05T15:37:31.930 回答
3

验证效率通常不是问题。您应该关注两种类型的验证:

  1. 输入验证,例如用户向服务器发送的内容是否是恶意的并打算破坏/闯入我的应用程序
  2. 业务验证,应该发生在您的业务逻辑中,并且应该是关于维护有效、一致的值。

跳过其中任何一个都是一个非常好的方法,最终导致应用程序被黑客入侵或严重损坏。

如果您使用的是 ASP.net,则有大量的库(主要来自 Microsoft)来执行前者,例如 Anti-XSS 库等。

后者将在实体本身的共享业务逻辑中最有效地执行。例如,您的用户实体不应允许 -1 的年龄。

于 2013-07-05T15:20:26.267 回答