Toward the end of our discussion about the Strategy design pattern, we briefly talked about the open/closed principle; I wanted to further my understanding of this concept, so I decided to do some research of my own. Today, I will summarize an article by Swedish systems architect Joel Abrahamsson entitled “A simple example of the Open/Closed Principle”.
Abrahamsson begins the article by summarizing the open/closed principle as the object oriented design principle that software entities should be open for extension, but closed for modification. This means that programmers should write code that doesn’t need to be modified when the program specifications change. He then explains that, when programming in Java, this principle is most often adhered to when implementing polymorphism and inheritance. We followed this principle in our first assignment of the class, when we refactored the original DuckSimulator program to utilize the Strategy design pattern. We realized, in our in-class discussion of the DuckSimulator, that adding behaviors to Ducks would force us to update the implementation of the main class as well as each Duck subclass. By refactoring the code to implement an interface in independent behavior classes – and then applying those behaviors to Ducks in the form of “setters” – we opened the program for extension and left it closed for modification. Abrahamsson then gives his own example of how the open/closed principle can improve a program that calculates the area of shapes. The idea is that, if the open/closed principle is not adhered to in the implementation of a program like this, it is susceptible to rapid growth as functionality is added to calculate the area of more and more shapes.
(Note: This is clearly not a Java implementation.)
public double Area(object[] shapes) { double area = 0; foreach (var shape in shapes) { if (shape is Rectangle) { Rectangle rectangle = (Rectangle) shape; area += rectangle.Width*rectangle.Height; } else { Circle circle = (Circle)shape; area += circle.Radius * circle.Radius * Math.PI; } } return area; }
( Abrahamsson’s implementation of an area calculator that does not adhere to the open/closed principle. )
public abstract class Shape { public abstract double Area(); }
public class Rectangle : Shape { public double Width { get; set; } public double Height { get; set; } public override double Area() { return Width*Height; } }
public class Circle : Shape { public double Radius { get; set; } public override double Area() { return Radius*Radius*Math.PI; } }
public double Area(Shape[] shapes) { double area = 0; foreach (var shape in shapes) { area += shape.Area(); } return area; }
( Abrahamsson’s implementation of an area calculator that adheres to the open/closed principle. )
Abrahamsson ends the article by sharing his thoughts on when the open/closed principle should be adhered to. He believes that the primary focus of any good programmer should be to write code well enough that it doesn’t need to be repeatedly modified as the program grows. Conversely, he says that the context of each situation should be considered because unnecessarily applying the open/closed principle can sometimes lead to an overly complex design. I have always known that it is probably good practice to write code that is prepared for the requirements of the program to change, and this principle confirmed that idea. From this point forward, I will take the open/closed principle into consideration when tackling new projects.
From the blog CS@Worcester – by Ryan Marcelonis and used with permission of the author. All other rights reserved by the author.