Hmm, I think I should provide a more detailed (boring) explanation on the reasons why the default constructor is in the list of my most hated anti-patterns.
Back to basics
OO theory provided us with the gift of encapsulation: the ability to hide details of the internal structure of an object by providing access control to attribute and methods thus allowing to split the exposed behaviour of an object (its interface) from the internal structure (the implementation).
The rule of thumb says that you should define all of your attributes private, and then provide public methods for the interactions with other classes. It’s a common practice (a short way to say that I quite disagree with that, but this will drive me out of scope) to provide also public methods to access the properties of the object, in a JavaBeans like fashion.
JavaBeans specification
JavaBeans specification defined that a special category of java objects, the Beans, was identified by the following properties
- presence of a public parameterless constructor
- presence of public methods to access properties of the object, such as getters and setters in the form of getPropertyName() and setPropertyName(…)
Unfortunately, what most of the developers forgot was that java beans specification were meant to be used by tools like IDEs (one of the first implementation of the spec was used to add user defined graphics object in a graphical environment, so that you could create your components and add them to the JBuilder palette) or frameworks like Castor or Hibernate. Java Server Pages also use Java Beans as the underlying object for the pages. Java Beans specification allowed tools to dynamically access properties of given (and unknown) objects by relying on introspection, driven by the coding conventions mentioned above.
Having a commonly accepted syntax for accessor methods, was a good result indeed, but was largely abused. But this gave new popularity to the empty constructor anti-pattern.
Why is the empty constructor evil?
Ok, I need a basic example to start from. Suppose we have a simple class like this:
public class Person {
private String name;
private String surname;
public Person() {}
public String getName() { return this.name; }
public String getSurname() {return this.surname; }
public void setName(String name) { this.name = name; }
public void setSurname(String surname) { this.surname = surname; }
}
By creating an empty object, and then setting its property in a
Person p = new Person();fashion, we make a series of mistakes. At the end of the execution the result is just like
p.setName(“John”);
p.setSurname(“Smith);
Person p = new Person(“John”,”Smith”);assuming that we defined in Person.java a constructor like
Public Person (String name, String surname) {But unfortunately the result it’s not the same during the execution. In other words object initialization is not atomic in the former case, while it is atomic in the latter. Moreover, the second constructor encapsulates creation logic, knowing that you need both name and surname to have a fully functional Person object. Using the default constructor, anybody trying to initialize the class should be aware of the internal details of the Person class to initialize the class.
this.name = name;
this.surname = surname;
}
Suppose then that you need instances of your class in different areas of your software. Every time, you should create your objects in the correct fashion. To do so, you normally would use the best replacement for OOP: cut & paste (which is a form of reuse after all…). Suppose then that you need to add an extra attribute to the Person class, namely birthDate, and that this attribute is required. Guess what? Changing the robust constructor to
public Person (String name, String surname, Date birthDate) {raises compilation problem on every invocation of the constructor. Is this a bad thing? Not at all! It allows you to correct every single invocation. It can be a simple job, or a tricky one (if the newly needed parameter is not so easy to get), but it ensures that the object is in the correct state. The other way round raises no compilation error, you know that you have to correct the code anyway, so you have the following options:
this.name = name;
this.surname = surname;
this.birthDate = birthDate;
}
- search every instance of new Person() throughout the code base and check that all the three setters are called (do you think that it’s fastest than fixing the compile errors?)
- search some known instance of new Person(), add the new setter, and have a coffee
- add the setter where we need it and let somebody else deal with the nullPointerException they are 99% likely to get.
What really puzzles me is that, every in the simplest situation, invoking the default constructor is almost always wrong! If your class has attributes, then you should favour a robust constructor instead, and makes you write less code. If it doesn’t have one, then maybe you don’t need a constructor at all but you could access static methods or use a factory to create your object. The only reason why we abuse so much this anti-pattern is just because we are damn lazy.
Tags: Java, OOP, AntiPatterns
1 comment:
I completely agree with this.
I've developed for years as a hobby and have always avoided javabeans intuitively. Now I do this as a job and I was amazed to find out that working with empty constructors is the norm in "professional" java development.
Post a Comment