Chain Constructors is another refactoring from the book Refactoring to Patterns, it helps remove duplication in constructor overloads. This should be a very familiar refactoring for most developers. Catch all constructors are produced, and hopefully minimized, that other constructors rely on either directly or indirectly.
Mechanics
public class Line { ... private Line() { Style = LineStyle.Solid; Color = LineColor.Black; Thickness = 1; } private Line(LineStyle style, LineColor color, int thickness) { Style = style; Color = color; Thickness = thickness; } ... }These can be chained together, to reduce redundant code and leave a common spot for future property initializations.
public class Line { ... private Line() : this(LineStyle.Solid, LineColor.Black, 1) { } private Line(LineStyle style, LineColor color, int thickness) { Style = style; Color = color; Thickness = thickness; } ... }
Pros
Lately, I’ve been engulfed in several design books, one of which is Refactoring to Patterns by Joshua Kerievsky. The book is an extension of Martin Fowler’s book, Refactoring. It emphasizes learning to use patterns by working with existing code, identifying code that “smells” and then finding a pattern to refactor the code “to, towards or away from.” Smelly code typically involves duplication, complexity and/or ambiguity.
Joshua believes, rather than heavy pattern use up front, to let code evolve into or away from patterns. I thought it might be fun to share some of these refactorings and examples from my code past (gasp).
One of the first refactorings, Replace Constructors with Creation Methods. Simply put, when we have lots of overloads or parameters for our constructor(s), ambiguity arises as to the intended use. Often we add conditional plumbing to construct different instances of the same class with different run time behavior. The problem is that the constructors are called the same way and we have no way of providing intention through naming conventions like we can with methods and other members.
I went back to some old code and found an example of where this refactoring could help. The code was doing various types of charting by abstracting the creation of lines to separate instances before rendering the chart. One of the classes, scrubbed, had constructors as follows:
public class Line { ... public Line(int year, int month, int productId) { Initialize(year, month, productId); LoadData(); } public Line(int month, int productId) { Initialize(2000, month, productId); } ... }
The first overload has an extra parameter to provide the year, that is all a user of this class would see when they come across instantiations of this class. With out digging deeper they wouldn’t realize that one overload queries and loads actual data points for the specified product while the other was intended to provide a way to manipulate and map existing lines to new lines.
The refactoring seeks to use methods to create instances of the class and subsequently, if appropriate, hiding the original constructors so users are forced to use the new, hopefully less ambiguous, Creation Methods.
Every refactoring has a set of steps, which should be performed one at at time until the desired changes are complete. Joshua emphasizes that not all steps must be completed. Sometimes only part of a refactoring is sufficient to clean up smelly code.
The “Mechanics,” as Joshua refers to in his book, are as follows:
Line line = new Line(2009, 2, productId);
Line line = CreateLine(2009, 2, productId); ... public static Line CreateLine(int month, int year, int productId) { return new Line(month, year, productId); }
Line line = Line.CreateLine(2009, 2, productId); ... public class Line { ... public static Line CreateLine(int month, int year, int productId) { return new Line(month, year, productId); } ... }
public class Line { ... private Line(int year, int month, int productId) { Initialize(year, month, productId); LoadData(); } ... }
public class Line { ... private Line(int year, int month, int productId) { Initialize(year, month, productId); LoadData(); }private Line(int month, int productId) { Initialize(2000, month, productId); }
public static Line CreateLine(int month, int year, int productId) { return new Line(month, year, productId); }
public static Line CreateMappingLine(int year, int productId) { return new Line(year, productId); } ... }