Thursday, September 23, 2004 - Posts

Replacing Constructors with Creation Methods is a bad idea

One of the patterns in Joshua Kerievsky’s “Refactoring to Patterns” is “Replace Constructors with Creation Methods”. The motivation behind this pattern is that programmers might be confused by classes with multiple constructors and that constructors do not communicate their intention very well. This is true; I have often found myself poking around in classes for the right constructor to use. Furthermore, overloaded methods in general have shortcomings since it is impossible to have two methods or constructors with the same signature.
The proposed solution to tackle the confusion caused by having multiple constructors on a class is to exchange the overloaded constructors with descriptive creation methods. When the creation methods are in place all calls to the different constructors should be exchanged with calls to the corresponding creation method. The different constructors should then be consolidated in one single private constructor used by all creation methods.
The example in the book uses a Loan class with multiple constructors and swaps these with static methods that create different kinds of loans such as term and revolver loans.

The listed benefits are:

  • Creation methods communicate available instances better than constructors.
  • Bypasses the signature limitations.
  • Makes it easier to discover unused constructors.

Nonstandard object creation is the only liability recognized in the book. There are additional important liabilities. The refactoring changes the public interface. All client code must be changed to use the creation methods instead of the new keyword.
The class is rendered unserializable after the refactoring since classes must have a public constructor in order to be serializable.

There is a much better solution to the issues this pattern addresses. All developers are familiar with it; inheritance.
Instead of re-implementing the “is-a” relationship of inheritance with methods, one could create different Loan classes that would describe the different types of loans much better than the creational methods do.
For instance one could simply create a RevolverLoan class that extends the Loan class. This class could have fewer constructors that make sense in the business context a revolver loan is used.
The signature limitations would be addressed as well since a subclass would have fewer and more precise constructors eliminating the need for different constructors with the same signature.
The argument that unused constructors are discovered more easily is based on that every object creation has to be replaced. Unless you have complete control over the client code, this is not a benefit at all, it is a liability. Most modern development environments will automatically identify unused code, static analyzers will do the same and if you have proper unit testing in place, code coverage tools will do it as well.
If the original base class is kept as is, the existing client code does not have to be changed since it can keep on creating instances of the original class as usual. New client code can start using the different subclasses and client code can be refactored to use the specific subclasses over time. When the base class no longer is created directly it can be made abstract if this is appropriate.

Refactoring to patterns is a great idea, but one has to be careful so one does not refactor to anti-patterns.