【问题标题】:How to validate constructor parameters on Java?如何在 Java 上验证构造函数参数?
【发布时间】:2018-08-26 08:47:55
【问题描述】:

假设我们使用 Java SE(没有库)并且有以下情况。我们有一堂课:

public class DriverInfo {
    private final int age;

    public DriverInfo(int age) {
        this.age = age;
    }

    // getter here
}

在某些国家/地区,您必须年满 18 岁才能开车。换句话说 - 我们需要对 age 参数进行一些验证。比如:

if (age < 18) {
    throw new IllegalArgumentException("Age is not valid!");
}

所以,我的问题是 - 这个验证应该在哪里?我的想法如下:

  • 在构造函数中添加上述 if()。如果从代码的多个位置调用构造函数,则验证将起作用。我担心的是类构造函数(恕我直言)不应该包含任何逻辑。我说的对吗?
  • 验证构造函数之外的某个地方 - 无论是工厂方法、构建器类等。但是我们迫使开发人员不要使用构造器来实例化类,而是使用一些人工工厂/构建器。
  • 使用Validate 验证构造函数参数。它不是标准的 Java,但我看到有人这样做。对我来说,它看起来不正确,因为我们正在向构造函数添加逻辑 - 这看起来不适合我。
  • 我错过了任何其他好方法吗?

任何想法将不胜感激。有没有人可以提出一个最佳实践来处理所描述的住所情况?

【问题讨论】:

  • 你应该在将值传递给构造函数之前检查年龄。
  • 在构造函数中抛出异常完全没问题。但在这样做之前,您应该释放获得的资源,例如关闭流。
  • 对此有太多不同的方法。根据您运行此代码的方式、您使用的框架,您将获得不同的标准实践。 Web 框架、Java EE 等都为您提供了执行此操作的方法,甚至不需要输入 if 条件。您知道您的验证,您正在寻找的是为您的平台/框架进行验证的适当方法。如果您的“框架”没有标准方法,那么您的建议就是完美的(硬编码部分除外)
  • 通过在构造函数中进行验证并在无法以有效状态构造对象时抛出异常来确保所有对象都在有效状态下构造。在构造函数中包含逻辑非常好,并且在 Java API 和第三方库中的许多类中都完成了。例如FileReader constructors 读取文件系统以搜索文件并确保文件可读然后打开它。否则他们会抛出异常。

标签: java validation


【解决方案1】:

构造函数中的验证完全没问题。这个“构造函数中没有逻辑”规则不适用于它,不幸的是它的措辞有点。 构造函数的任务是从外部获取依赖项(依赖注入),确保它们有效,然后可能将它们存储在实例属性中 - 简而言之创建有效实例

这样,有效和无效的规则被保存在对象内部,而不是在某些工厂中分离。

如果实例无效,那么抛出一个解释参数错误的异常是绝对可以的。它可以防止无效实例在系统中漫游。

同样,对于不可变对象,您可以保证所有现有实例始终有效,这很棒。

对对象有一些验证方法是可能的并且很有用,但我更喜欢任何一天的构造函数验证。但如果不可能,那总比没有好,它仍然在对象内保留有效性规则。就像某些框架为您构造可变对象并需要无参数构造函数时......您可以忘记调用它,但总比没有好。

【讨论】:

    【解决方案2】:

    我认为您可以“也”将Builder 模式用于此类用例。虽然构造函数验证非常好,而且通常 Builder 倾向于引入大量的样板代码,但是如果你想避免在构造函数内部进行验证,并且还想拥有不可变的类(没有 setter 验证),那么你可以给 builder 一个尝试。

    当面对许多构造函数参数时,通常应该使用构建器。你可以阅读更多关于建设者here

    public class DriverInfo {
    
        private final int age;
    
        //more parameters here
    
        private DriverInfo(int age) {
            this.age = age;
        }
    
        public int getAge() {
            return age;
        }
    
        public static DriverInfoBuilder newBuilder() {
            return new DriverInfoBuilder();
        }
    
        public static final class DriverInfoBuilder {
    
            private int age;
    
            private DriverInfoBuilder() {
            }
    
            public DriverInfoBuilder age(int age) {
                this.age = age;
                return this;
            }
    
            public DriverInfo build() {
                if (age < 18) {
                    throw new IllegalArgumentException("Age is not valid!");
                }
    
                //other validations.
    
                return new DriverInfo(age);
            }
        }
    }
    

    同样有很多方法可以做到这一点,这里没有对错。它更多地是关于一个人喜欢什么,以及其他程序员是否可以阅读。

    【讨论】:

      【解决方案3】:

      在我看来,您应该在 DriverInfo 类中有一个方法,例如:

      public boolean isDriverLegalAge(int age){
          if (age < 18) {
              return false;
          } else {
              return true;
          }
      }
      

      构造函数只能用于构建对象。该对象内部与逻辑相关的所有内容都应该在方法内部。

      有关构造函数用途的更多信息,请参阅此链接:https://stackoverflow.com/a/19941847/2497743

      【讨论】:

        【解决方案4】:

        我认为您可以使用另外两种技术。

        1. 将变量传递给 super() 并在父类中验证它们。 例如

          超级(年龄)

        2. 您可以使用 getterssetters 并在那里验证变量。我尝试在 java 中使用 setter。

          public class validate_object_variable {
          private int age;    
          public int getAge() {
              return age;
          }
          
          public void setAge(int age) {
              if(this.age >= 18)
              this.age = age;
              else {
                  throw new IllegalArgumentException("Age is not valid!");
              }
          }
          
           public static void main(String args[]) {
                  validate_object_variable obj = new validate_object_variable();
                  obj.setAge(10);
                  System.out.println(obj.getAge());
           }
           }
          

          这使我能够以有效异常干净地退出调用者。

          输出

          我认为在构造函数中抛出异常不是一个好主意。但在不妨碍对象创建的情况下,单独使用 setter 验证变量是一个不错的选择。

        【讨论】:

        • 这个答案假设我们正在构建的类是“可变的”,这是不好的。其次,您希望在超类中进行验证,这基本上意味着仅仅为了验证而扩展一个类。这些技术不好。
        • @Sneh 我知道超类技术取决于天气子类是否从对象类以外的父类继承?而且我需要澄清上面的代码如何假设类是“可变的”?
        • 你知道什么是可变性吗?您正在为类提供一个设置器,它允许在构造后随时更改“年龄”。至于您的超类解决方案,您并没有解决问题,而是从一个地方移动到另一个地方(假设我们试图避免在构造函数中进行验证)
        猜你喜欢
        • 2022-06-18
        • 1970-01-01
        • 1970-01-01
        • 2010-12-27
        • 2017-02-07
        • 1970-01-01
        • 2012-09-18
        • 2019-04-02
        • 1970-01-01
        相关资源
        最近更新 更多