【问题标题】:Validating construcor parameters determined by another parameter验证由另一个参数确定的构造函数参数
【发布时间】:2017-11-04 20:27:44
【问题描述】:

我有以下enum

public enum StudentType {
    Domestic, International;
}

还有一个具有以下构造函数的 Student 类:

//Left out additonal parameters and validation
public Student(StudentType type, List<String> documents){

  if(type == null){
     throw new IllegalArgumentException("You must provide Student type");
  }
  this.type = type;
  this.documents = this.validateList(documents);
}

还有一个验证列表的私有方法:

private List<String> validateList(List<String> validate){
   if(this.type == StudentType.Domestic && validate.isEmpty()){
        return validate;
    }
    else 
      if(this.type == StudentType.Domestic && !validate.isEmpty()){
        return Collections.emptyList();
   }
   return new ArrayList<String>(validate);
}

我的计划是让 Student 类不可变。

说明:

只有国际学生需要文件(护照等)。国内学生不需要任何文件。

在我的constructor 中,我检查以确保类型不是null,客户必须提供国内或国际。

在我的私人 validateList method 中,我检查学生类型,以及 List 是否为空。如果是国内且为空,则返回列表,如果是domensitc且不为空,则返回空集合,否则返回文档的ArrayList&lt;String&gt;

我的问题是检查私有方法中的类型是否是代码异味?我担心的是一个参数(文件列表)由学生类型确定/验证。如果检查私有方法中的类型是代码异味,我应该怎么做?

【问题讨论】:

    标签: java oop parameters constructor


    【解决方案1】:

    我发现您发布的代码存在一些问题,但不是您认为的问题。

    首先,我们不知道documents 来自哪里。我认为这只是代码中的一个错误,实际上它是第二个参数。

    现在,代码:

    private List<String> validateList(List<String> validate){
       if(this.type == StudentType.Domestic && validate.isEmpty()){
            return validate;
        }
        else 
          if(this.type == StudentType.Domestic && !validate.isEmpty()){
            return Collections.emptyList();
       }
       return new ArrayList<String>(validate);
    }
    

    首先,如果参数无效(就像您对 null 学生类型所做的那样),而不是拒绝参数,您只需忽略参数并使用空列表代替。一般来说,这不是一个好主意。如果调用者传递了一个非空列表,它当然不希望这个列表被默默地忽略。如果列表应该为空,则通过抛出异常来拒绝非空列表。

    第二:最后一行代码表明您想要制作作为参数传递的列表的防御性副本。但是您不会在 if 块的第一个分支中这样做。并且由于这两个分支无论如何都包含存储一个空集合,因此可以将其替换为

    private List<String> validateList(List<String> validate) {
        return this.type == StudentType.Domestic ? Collections.emptyList() : new ArrayList<>(validate);
    }
    

    最后,(回到我的第一点),既然如果学生类型是国内的话,调用者永远不应该传递一个非空列表,你可以通过使用两个工厂方法而不是构造函数来实现这一点:

    private Student(StudentType type, List<String> documents) {
        this.type = type;
        this.documents = documents;
    }
    
    public static Student createDomestic() {
        return new Student(StudentType.DOMESTIC), Collections.emptyList());
    }
    
    public static Student createInternational(List<Document> documents) {
        return new Student(StudentType.INTERNATIONAL, new ArrayList<>(documents);
    }
    

    【讨论】:

    • 我更正了我的代码,文件应该是构造函数参数。如果学生是国内的,那么不需要任何类型的文档,List集合应该是空的。
    • 如果我只想用构造函数来做这个,没有工厂方法,我该怎么做?
    • 正如我所说,如果列表预期为空,但不是,则抛出异常。您还可以提供一个没有文档的重载构造函数。但是为什么不能使用工厂方法呢?这是一个任意的约束。
    • 我可以使用工厂方法,只是出于好奇想知道是否可以不使用它们。
    【解决方案2】:

    我会用以下方式重写validateList 方法:

    private List<String> validateList(List<String> validate){
       if(this.type == StudentType.Domestic){
            return Collections.emptyList();
       }
       return new ArrayList<String>(validate);
    }
    

    说明

    如果学生类型是国内的,只要您只需要不返回任何文件,我将两个条件合二为一。国内学生有没有证件对我们来说并不重要,我们不需要检查。

    【讨论】:

    • 你的回答和上面那个真的很好。如果我可以投票,我会(新帐户),因为我以前写过这样的方法。对我来说,如果客户端首先传递一个空列表,忽略它并返回一个新的空列表,这似乎有点奇怪。
    • 谢谢。这个想法是使代码尽可能简单。使用返回 Collections.emptyList();您明确表示您的意图,无论如何,但是当学生在国内时,他应该没有文件。是的,保留初始数组以防万一它是空的,效率并不高。但我更喜欢易读性和简单性,而不是细微的性能改进。我猜,这是 Java 世界中的一种常见做法。无论如何,我更喜欢 JB Nizet 的答案而不是我的答案,因为它更详尽。
    猜你喜欢
    • 1970-01-01
    • 2022-06-18
    • 1970-01-01
    • 2010-12-27
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    • 2012-07-28
    • 2019-04-02
    相关资源
    最近更新 更多