【问题标题】:OOP-Design for validating user input用于验证用户输入的 OOP 设计
【发布时间】:2018-01-09 11:48:59
【问题描述】:

目前,我尝试在设计一些东西时考虑到面向对象的原则。因此,假设在处理用户输入之前,我需要对其进行验证。根据 OO,单独的 Validator 类将是正确的方法。如下所示:

    public class Validator{
      public void validate(String input) throws ValidationException{
        if (input.equals("")) throw new ValidationException("Input was empty");
      }
    }

然后,我的处理类(之前通过依赖注入获取验证器对象)将调用validator.validate(input)

这个设计的一个优点是,

  1. 我的处理类可以通过 DI 获得验证器的模拟,这使得测试更容易
  2. Validator 类可以独立测试

但是,我的疑虑在于验证器的设计。根据OO,它错过了某种状态。通过这种设计,它是 util 类,验证方法可以是static。而且我读了很多次(静态)Util 类是糟糕的 OO 设计。那么,如何在保持我提到的两个优势的同时,通过更多的 OO 来做到这一点呢?

PS.:也许,OO 对这类问题只是一个糟糕的解决方案。不过,我想看看 OO 解决方案的样子并形成我自己的看法。

【问题讨论】:

  • 我认为最好将所有验证错误写入集合并返回。然后您可以根据需要处理错误(异常、正确的 html 页面、json 响应等)。假设用户必须填写一些包含 10 个字段的表单 - 您的设计将在第一个字段上抛出异常,并且不告诉任何其他 9 个字段。所以你的验证器的目的只是检查数据和返回结果,其他类会写一个响应。
  • 你说得对,我以前没有想过这个。

标签: java oop coding-style


【解决方案1】:

您示例中的验证器没有状态(也不需要任何状态),但另一个验证器可能需要一个状态(例如使用格式):

例子:

public class RegExValidator {
    private Pattern pattern;

    public RegExValidator(String re) {
        pattern = Pattern.compile(re);
    }

    public void validate(String input) throws ValidationException {
        if (!pattern.matcher(input).matches()) {
            throw new ValidationException("Invalid syntax [" + input + "]");
        }
    }
}

【讨论】:

    【解决方案2】:

    专注于您问题的 OOP 方面(而不是如果异常是处理您的验证的正确方法的问题):

    为什么只有一个验证器?

    interface Validator<T> {
        void validate(T toValidate) throws ValidationException;
    }
    

    将使您能够编写可以验证任何类T 并且非常可测试的类。您的验证器如下所示:

    class EmptyStringValidator implements Validator<String> {
        public void validate(String toValidate) {
            if(toValidate == null || toValidate.isEmpty()) throw new ValidationException("empty!!!");
        }
    }
    

    你可以很容易地测试它。 事实上,如果您使用的是 Java 8,这将是一个函数式接口,因此一个实用程序类可以承载多个验证器:

    class ValidationUtil {
        public static void emptyString(String val) // same code as above
    }
    

    ValidationUtil::emptyString 将实现Validator&lt;String&gt;。 您可以将多个验证器与复合模式结合起来。

    如果你需要的话,你也可以有一个带有状态的验证器......

    class ListIsSortedValidator implements Validator<Integer> {
        private int lastInt = Integer.MIN_VALUE;
        public void validate(Integer val) throw ValidationException {
            if (val < lastInt) throw new ValidationException("not sorted");
            lastInt = val;
        }
    }
    

    你可以用来验证一个列表:

    List<Integer> list = createList();
    Validator<Integer> validator = new ListIsSortedValidator();
    list.forEach(validator::validate);
    

    【讨论】:

      【解决方案3】:

      这当然取决于具体情况,但我认为你的直觉是正确的。这种设计可能更加面向对象。

      不仅仅是Validator 没有状态,这是一个纯粹的机械指标,表明它可能不是一个正确的抽象,而是名称本身告诉我们一些事情。通常Validator(甚至EmptyStringValidator)不是问题域的一部分。当您必须创建纯技术的东西时,这总是一个不好的迹象(尽管有时它是两害相权取其轻)。

      我假设您不是在编写 Web 框架,而是在尝试编写具有某个域的应用程序。例如它有用户注册。然后,RegistrationForm 是问题域的一部分。用户知道“注册表单”,你可以谈论它,他们就会明白你的意思。

      在这种情况下,一个面向对象的验证解决方案是这个对象在“提交”自己的过程中负责自己的验证。

      public final class RegistrationForm extends Form {
          ...
          @Override
          public void submit() {
              // Do validation here
              // Set input fields to error if there are problems
              // If everything ok do logic
          }
      }
      

      我知道这不是网络框架通常看到甚至支持的解决方案。但它面向对象解决方案的样子。

      要始终牢记的两个要点是:

      1. 不要从对象中“获取”数据,而是要求他们做某事。这与其他任何内容一样适用于 UI 代码。
      2. 当对象专注于有意义的事情(即问题域)时,OO 才有意义。避免过度代表技术(不重要)对象,例如 Validator(如果这不是您的应用程序的域)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-09-11
        • 1970-01-01
        相关资源
        最近更新 更多