SOLID Design Principles: The Open Closed

Avinash Dhumal
4 min readSep 7, 2022

--

Introduction

In my last article SOLID Design Principles: The Single Responsibility we talked about the single responsibility of solid design principles. Before we move onto the open closed principle, please read the article to understand what SOLID design principles and its benefits.

In today’s article, I am going to talk about the open closed principle, what is the purpose of implementing it with a real time example.

The Open Closed principle states that — software components such as classes, methods, functions, modules should be open for extension but should be closed for modification which is nothing but software components are further allowed to extend the behavior without modifying current behavior.

Let’s understand why we really need to implement this open and closed principle. If you are allowing the modification to current or existing classes or methods to keep accommodating new changes then it leads to following challenges:

  1. Your classes or methods gradually become big in size as you keep adding new logic in existing code.
  2. Your classes or methods logic will become unmanageable after a certain point and will result in more efforts in making changes in existing logic.
  3. Your classes or methods will require a complete testing on its functionality though you add or update a few lines of code which results in more testing efforts.

Today, we are going to understand how the “Open Closed Principle” works in software development.

Let’s take the same example which we used in the previous article of Single Responsibility principle. We have a Report Class which is responsible for generating reports.

Let’s say GenerateReport() method is creating a report in excel file. We have a new requirement and now we have to generate a report in the doc file. So, how can we adjust our logic? Either you can add a new method say GenerateDocReport() in Report class or add additional logic in GenerateReport() method to generate doc file. Something like below:

public class Report
{
public void GenerateReport()
{
//excel report logic
// AND
//doc report logic
}
}

OR

public class Report
{
public void GenerateReport()
{
//excel report logic
}
public void GenerateDocReport()
{
//doc report logic
}
}

Both solutions are not the best practice as you are bringing the challenges which we mentioned above.

So, how can we solve this by implementing open closed principle? Let’s walk through the following class diagram.

We have defined an IReport interface which has a GenerateReport() method. And for each kind of report generated such as Excel, doc, we have written down separate classes ExcelReport and DocReport which are inheriting IReport interface and implementing GenerateReport().

So let’s understand how it is helping us to achieve open closed principle. Now we can clearly say that the classes ExcelReport and DocReport are closed for implementation as their main responsibility is to generate reports in their respective formats. But if we wish to implement a new type of report, say pdf, then it allows us to extend the functionality by creating a new class called PDFReport and inheriting IReport interface and implementing GenerateReport() method.

Now let’s understand how we should be using this GenerateReport() method to extract the report depending upon need.

public interface IReport
{
void GenerateReport();
}
public class ExcelReport : IReport
{
public void GenerateReport()
{
//logic to generate Excel report
}
}
public class DocReport : IReport
{
public void GenerateReport()
{
//logic to generate Doc report
}
}
public class PDFReport : IReport
{
public void GenerateReport()
{
//logic to generate PDF report
}
}
static void Main(string[] args)
{
IReport report = new ExcelReport();
report.GenerateReport(); //This will call method from ExcelReport class
report = new DocReport();
report.GenerateReport(); //This will call method from DocReport class
report = new PDFReport();
report.GenerateReport(); //This will call method from PDFReport class
}

The important thumb rule to remember here is, we are not initiating separate objects to call each respective class’s methods. We are declaring an IReport interface object and initializing these respective classes using the same object whenever I need to generate reports with respective formats.

With this approach we can now say that each class is closed for further implementation but is open for extension by inheriting the same interface and implementing new logic. This helps us to segregate the logic and easier to maintain, write the code and test.

Conclusion

With the above example, we have now understood how to refactor the code and achieve the Open Closed principle. With the help of the open closed principle it helps us to allow extending the current functionality without affecting the existing functionality.

If you enjoy the content I create and would like to show your appreciation, you can buy me a coffee!

--

--

Avinash Dhumal

13+ years of software architect, design, development, management, and support experience in Microsoft technologies using Azure & AWS Cloud services.