Where to place comprobations in Java?

Víctor :

I've come up with this problem over and over again when creating objects that must verify conditions. Should the checks be placed before trying to create the object or in the constructor of the object itself?

To illustrate it better, here's an example: let's say that we have a student manager, a professor, who adds students objects to a list of them. When creating a new student object, we must check that his name is maximum 20 characters long.

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 
    }
}

So basically my question would be, should the checks be done in "Place 1", before trying to create the student, or in "Place 2", in the constructor of students.

Basil Bourque :

Objects take responsibility for themselves

Generally in object-oriented programming (OOP), we want objects to be responsible for themselves. Business rules regarding the integrity of their internal state should be handled, well, internally (or delegated to a builder -- see below). This idea is part of what is known formally in OOP as encapsulation.

So in your example, the Professor class should not worrying about the Student class’ rules such as length of the student’s name. The Student class should be enforcing its own integrity. We want the logic for these integrity rules to be located in one single place, not spread out over your entire app.

Indeed, the Professor class should not be instantiating the Student object. Implied in your example is that there must be some other party that is assigning students to a professor. Perhaps a Tutorial object that is responsible for tracking the assignment and progress of a few students being supervised by a professor. This Tutorial should be instantiating the Student objects, or passing on the Student objects received from some other source such as a database-service object.

By the time the Student objects reach the Professor, they should be valid. The Professor class should not be concerned with what makes a Student valid or not. The Professor should only be concerned with what makes a Professor valid or not.

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 ) ;
    }
}

Besides the idea of objects being responsible for themselves, another reason for Professor to not be instantiating Student objects is to facilitate testing. If the Student objects come from some other source, that source can provide faux objects using a Student class or interface that is not yet finished, has certain functionality disabled (such as database access), or is substituted with bogus data designed for testing a scenario.

Builder pattern

If you have multiple properties that need verifying in order to instantiate a new object, you may want to use the Builder pattern. You define an additional class, such as StudentBuilder that has methods for each of the parts needed to make a student.

Often, these methods all return the same StudentBuilder object to facilitate call-chaining.

Different folks have different styles for a builder. One way is to provide a validity-checking method, and perhaps a method that provides a list of problems that prevent building the desired object.

Some people use a word like with rather than the accessor method set to make clear that while we are temporarily setting a property on the builder, the real intention is to be setting a property on an object of another class.

StudentBuilder sb = new StudentBuilder().withFirstName( "Alice" ).withLastName( "Coleman" ).withEmail( "[email protected]" );
if( sb.isValid() ) {
    Student s = sb.build() ;
    …
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=99224&siteId=1