Saturday, September 13, 2014

Clean Code With Builders

To anyone who says that software engineering is not an art I say "Have you heard about design patterns?". Design patterns are like poetry to a software engineer. I never meant to write a post about them because tons of posts and books were already published on those. But following a talk I gave this week I understood that people are not familiar with the ways the "Builder" pattern can help them create a cleaner code.




What Is The Builder Pattern?

You can skip this part if you're familiar with the Builder pattern for creating immutable objects.

The "Builder" pattern helps us, not surprisingly, to build objects. It is often used for building immutable objects. For example, let's say we have this class:


public class Student {
    private final String givenName;
    private final String lastName;
    private final int averageGrade;
    private final int age;

    public Student (final String givenName, final String lastName, 
                    int averageGrade, int age) {
        this.givenName = givenName;
        this.lastName = lastName;
        this.averageGrade = averageGrade;
        this.age = age;
     }

     // Rest of the class is only getters with no setters here
}        

Then, if we want to split the assembly of the fields from the actual construction, we can use a builder in the following manner:

public class Student {
    private final String givenName;
    private final String lastName;
    private final int averageGrade;
    private final int age;

    public Student (final String givenName, final String lastName, 
                    int averageGrade, int age) {
        this.givenName = givenName;
        this.lastName = lastName;
        this.averageGrade = averageGrade;
        this.age = age;
     }

     // Rest of the class is only getters with no setters here

   public static class StudentBuilder {
 
     private String givenName;
     private String lastName;
     private int averageGrade;
     private int age;
        
     public StudentBuilder() { }

     public StudentBuilder withGivenName(final String givenName) {
       this.givenName = givenName;
       return this;
     }

     // The rest of the 'with' setters look the same...

     public Student build() {
       return new Student(givenName, lastName, averageGrade, age);
     }
  }
}        


This way we have a mutable inner class StudentBuilder which gathers the fields we need for the immutable class and when the time has come to create the immutable object we just call build() and we get the immutable object already fully constructed.

So what does that have to do with clean code?


Express Yourself With Builders

I'm sure you have seen a pattern similar to that in the past:
StudentGrades grades = new StudentGrades();
grades.setStudentId(97); // The student id in the DB
grades.setMath(84);
grades.setEnglish(92);
grades.setChemistry(75);
grades.setLiteratue(88);
grades.setGymnastic(55);
grades.setBiology(76);
grades.setHistory(81);

What we'll usually try to do next is something that will wrap these lines into a single line like this:
private StudentGrades createStudentGrades (int id, int math, int english,
                                           int chemistry, int literature,
                                           int gymnastic, int biology, int history) {
  StudentGrades grades = new StudentGrade();
  grades.setStudentId(id); // The student id in the DB
  grades.setMath(math);
  grades.setEnglish(english);
  grades.setChemistry(chemistry);
  grades.setLiteratue(literature);
  grades.setGymnastic(gymnastic);
  grades.setBiology(biology);
  grades.setHistory(history);
  return grades;
}
And indeed this will make the whole clutter of code into a one-liner:
StudentGrades grades = createStudentGrades(97, 84, 92, 75, 88, 55, 76, 81);
Well, this is great. Much less verbose. But without looking back on the createStudentGrades method, will you be able to tell what 75 stands for? Or 88?

This pattern is very hard to read. You have to scroll back and forth or at least open the tooltip of the method to understand what each number says.

Using the builder pattern can help us create something that is less verbose than the original version of the code (the one with the oh so many lines) and a bit more descriptive than this one liner. We'll create a builder for StudentGrades as follows:
// This doesn't have to be inner class now. It's not the immutability that we need the builder for. It's the readability
public StudentGradesBuilder { 
   int id;
   int math;
   int english;
   int chemistry;
   int literature;
   int gymnastic;
   int biology;
   int history;

   private StudentGradesBuilder() { 
     // Making the constructor private in order to enforce construction
     // with the readable static construction method.
   }

   public StudentGradesBuilder studentGrades() {
      return new StudentGradesBuilder();
   }

   public StudentGradesBuilder forUser(int id) {
      this.id = id;
      return this;
   }

   public StudentGradesBuilder withHistory(int history) {
     this.history = history;
     return this;
   }

   // Rest of setters

   public StudentGrades build() {
     StudentGrades grades = new StudentGrade();
     grades.setStudentId(id); 
     grades.setMath(math);
     grades.setEnglish(english);
     grades.setChemistry(chemistry);
     grades.setLiteratue(literature);
     grades.setGymnastic(gymnastic);
     grades.setBiology(biology);
     grades.setHistory(history);
     return grades;
   }
}

Now we can construct StudentGrades in a verbal, yet concise manner:
// Static importing of StudentGradesBuilder.studentGrades() method
// allows to call it directly which makes it readable like an English sentence
StudentGrades studentGrades = studentGrades().forUser(97).withMath(84)
                                             .withEnglish(92).withChemistry(75)
                                             .withLiterature(88).withGymnastic(55)
                                             .withBiology(76).withHistory(81)
                                             .build();

This can be read almost as an English sentence "studentGrades for user 97 with math 84...."

Don't know about you - but I really like it this way. This make me feel I read a sentence rather then trying to decrypt the meaning of a random group of numbers.


Find me on Twitter: @AviEtzioni


More interesting posts from this blog:

No comments:

Post a Comment