用Java在何处赞美?

时间:2018-10-31 23:34:04

标签: java

在创建必须验证条件的对象时,我一遍又一遍地提出了这个问题。应该在创建对象之前还是在对象本身的构造函数中进行检查?

为更好地说明问题,请举一个例子:假设我们有一个学生经理,一位教授,他将学生对象添加到其中。创建一个新的学生对象时,我们必须检查他的名字最长不超过20个字符。

class Professor{
    LinkedList<Student> studentsList;

    Professor(){
        studentsList = new LinkedList<Student>();
    }

    public Student addStudent(String studentName){
        // Place 1
        if (studentName.length <= 20)
            studentList.add(new Student(studentName));
        else
             // Do another thing
    }
}

class Student {
    String name;

    Student(String studentName){
        // Place 2
        if (studentName.length <= 20)
            name = studentName);
        else
            // Don't create the object and throw exception 
    }
}

所以基本上我的问题是,应该在创建学生之前在“地方1”中进行检查,还是在学生的构造函数中在“地方2”中进行检查。

2 个答案:

答案 0 :(得分:3)

在简单程序中,没有太大关系。在复杂的应用程序中,有许多因素决定了这一点:

  • 在某些情况下,具有无效值的对象是否仍然存在? (即使它们包含无效值,它们是否也有意义?)
  • 验证是否昂贵? (是否需要计算,网络连接或数据库操作?)
  • 可以验证吗? (我们已经拥有验证所需的所有信息吗?)
  • 验证是否有其自己的阶段,即与其他对象一起进行验证或针对其他对象进行验证?
  • 是否存在约定或要求?

等...

因此,大多数情况下,这将由体系结构或设计约束或与更大应用程序相关的其他因素来给出。在非常小的程序中,您可能找不到任何确定验证最佳位置的因素。

在上面显示的示例对象创建代码中,通常不会默默地跳过超过20个字符的值,但在这种情况下通常会引发异常。如果这是数据处理,而不是故意对少于20个字符的记录进行过滤,则您不想默默地忽略不合适的记录。 (想象一下,谁将手动检查为什么在1000条记录中为什么缺少5条记录,并且没有错误消息指出出了什么问题。所以也许您可以看到,上述方法无论如何都不实用。) / p>

答案 1 :(得分:3)

对象自行承担责任

通常在object-oriented programming (OOP)中,我们希望对象对自己负责。有关内部状态完整性的业务规则应在内部进行妥善处理(或委托给构建者-参见下文)。这个想法是OOP中正式称为encapsulation的一部分。

因此,在您的示例中,Professor班级不必担心Student班级的规则,例如学生姓名的长度。 Student类应加强其自身的完整性。我们希望这些完整性规则的逻辑位于一个位置,而不是散布在整个应用程序中。

实际上, Professor类应该实例化Student 对象。在您的示例中暗含的是,必须有其他人将学生分配给教授。也许是一个Tutorial对象,负责跟踪由教授指导的几个学生的作业和进度。该Tutorial应该实例化Student对象,或者传递从其他来源(例如数据库服务对象)接收到的Student对象。

Student对象到达Professor时,它们应该是有效的。 Professor类不应关注使Student有效的原因。 Professor只应关注使Professor有效的因素。

class Professor{
    List< Student > students;
    …
    public void addStudent( Student student ){
        Objects.requireNonNull​( student , "Received NULL rather than a Student object. Message # 68a0ff63-8379-4e4c-850f-e4e06bd8378a." ) ;  // Throw an exception if passed a null object.
        Objects.requireNonNull​( this.students , "Collection of Student objects is NULL. Message # c22d7b22-b450-4122-a4d6-61f92129569a." ) ;  // Throw an exception if the `students` list is not established.
        this.students.add( student ) ;
    }
}

除了对象负责自己的想法外,Professor不实例化Student对象的另一个原因是为了促进testing。如果Student对象来自其他来源,则该来源可以使用Student类或尚未完成的接口提供 faux 对象,并且禁用了某些功能(例如数据库访问),或被为测试场景而设计的虚假数据代替。

构建器模式

如果您有多个需要验证才能实例化新对象的属性,则可能要使用Builder pattern。您定义了一个附加类,例如StudentBuilder,该类具有针对学生所需的每个部分的方法。

通常,这些方法都返回相同的StudentBuilder对象以简化call-chaining

不同的人对于建筑商有不同的风格。一种方法是提供一种有效性检查方法,也许是一种提供一系列无法​​构建所需对象的问题的方法。

有些人使用with之类的词而不是访问器方法set来表明,虽然我们暂时在构建器上设置属性,但真正的目的是在另一个类的对象。

StudentBuilder sb = new StudentBuilder().withFirstName( "Alice" ).withLastName( "Coleman" ).withEmail( "x@y.com" );
if( sb.isValid() ) {
    Student s = sb.build() ;
    …
}