In the first post, we took a look at what the Open/Closed Principle is, why it is important, and how to use it. I’d like to continue the discussion with another example, and how the Strategy design pattern can help us out.
First, let’s take a look at the example code that we will be working with:
This example takes a Report object, iterates its fields, and formats each based on its FieldType. This example is very simple, but is enough to illustrates code that is rather typical. So let’s see where we can make some improvements.
The most obvious place where we will get burned is in the BuildReport method. This method violates both the Single Responsibility Principle (SRP) and the Open/Closed Principle (OCP). What happens when the client wants a new format type? We compound the violation of both, thus making the code harder to read, test, and maintain. Remember, we want to try and affect as little existing code as we can, and change behavior through new code. To start, let’s introduce a simple IFieldFormatter interface.
Now, let’s create the formatters that will replace the logic in the switch statement in our BuildReport method:
And now here is the updated BuildReport method:
This gets us closer to keeping in line with SRP and OCP.
The next step is to refactor that switch statement. In this case, I am going to move it into a new factory class:
You may be thinking that all I have really done is move the creation logic from one class to another, and technically you would be correct, but let us look a little deeper. Now our ReportBuilder can do just that, build reports without having to worry about low level details. We also have a class whose sole responsibility is to create formatters. This eliminates the need to update ReportBuilder every time we need to add a new formatter type.
Next, let’s update our ReportBuilder to accept an IReportFieldFormatterFactory parameter and update our BuildReport method:
Now when we need to add a new formatter, we can easily extend our existing factory and inject it into our ReportBuilder.
By adhering to the concepts behind the Open/Closed Principle we are able to modify existing functionality by adding new code and leaving the existing codebase alone. It also forces us to use other SOLID design principles which will keep us on the path to writing clean, flexible, testable, and maintainable code.