Open-Closed Principle in C#

Open-Closed Principle in C# with one real-time example

In this article, I am going to discuss the Open-Closed Principle in C# with one real-time example. Please read our previous article before proceeding to this article where we discussed the Single Responsibility Principle in C# with one real-time example. The letter O in SOLID stands for Open-Closed Principle which is also known as OCP. As part of this article, we are going to discuss the following pointers in detail.

  1. What is the Open-Closed Principle in C#?
  2. Implementation Guidelines for the Open-Closed Principle in C#
  3. What Problems you will get if you are not following the Open-Closed Principle?
  4. Example without using the Open-Closed Principle.
  5. Example using the Open-Closed Principle in C#.
What is the Open Closed Principle in C#?

The Open Closed Principle states that “software entities such as modules, classes, functions, etc. should be open for extension, but closed for modification“.

Let us understand the above definition in simple word. Here we need to understand two things. The first thing is Open for extension and the second thing is Closed for modification,

The Open for extension means we need to design the software modules/classes in such a way that the new responsibilities or functionalities should be added easily when new requirements come. On the other hand, Closed for modification means, we should not modify the class/module until we find some bugs.

The reason for this is, we have already developed a class/module and it has gone through the unit testing phase. So we should not have to change this as it affects the existing functionalities. In simple words, we can say that we should develop an entity in such a way that it should allow its behavior to be extended without altering its source code.

Implementation Guidelines for the Open Closed Principle in C#
  1. The easiest way to implement the Open Closed Principle in C# is to add the new functionalities by creating new derived classes which should be inherited from the original base class.
  2. Another way is to allow the client to access the original class with an abstract interface.
  3. So, at any given point of time when there is a change in requirement or any new requirement comes then instead of touching the existing functionality, it’s always better and suggested to create new derived classes and leave the original class implementation as it is.
Problems of Not following the Open Closed Principle in C#: 

If you are not following the Open Closed Principle during the application development process, then you may end up your application development with the following problems

  1. If you allow a class or function to add new logic then as a developer you need to test the entire functionalities which include the old functionalities as well as new the functionalities of the application.
  2. As a developer, it is also your responsibility to tell the QA (Quality Assurance) team about the changes in advance so that they can prepare themselves in advanced for regression testing along with the new feature testing.
  3. If you are not following the Open Closed Principle, then it also breaks the Single Responsibility Principle as the class or module is going to perform multiple responsibilities.
  4. If you are implementing all the functionalities in a single class, then the maintenance of the class becomes very difficult.

Because of the above key points, we need to follow the open-closed principle in C# while developing the application.

Let us understand the Open Closed Principle in C# with one example.

Please have a look at the following diagram.

Violating open Closed principle in C#

As you can see in the above image, within the Invoice class we have created the GetInvoiceDiscount() method. As part of that GetInvoiceDiscount() method, we are calculating the final amount based on the Invoice type. As of now, we have two Invoice Type such as Final Invoice and Proposed Invoice. So we have implemented the logic using if else. Tomorrow, if one more Invoice Type comes into picture then we need to modify the GetInvoiceDiscount() method logic by adding another else if block to the source code. As we are changing the source code for the new requirement, we are violating the Open-Closed principle in C#. 

Example without using the Open Closed Principle
namespace SOLID_PRINCIPLES.OCP
{
    public class Invoice
    {        
        public double GetInvoiceDiscount(double amount, InvoiceType invoiceType)
        {
            double finalAmount = 0;
            if (invoiceType == InvoiceType.FinalInvoice)
            {
                finalAmount = amount - 100;
            }
            else if (invoiceType == InvoiceType.ProposedInvoice)
            {
                finalAmount = amount - 50;
            }
            return finalAmount;
        }
    }
    public enum InvoiceType
    {
        FinalInvoice,
        ProposedInvoice
    };
}

The problem with the above example is that if we want to add another new invoice type, then we need to add one more “if” condition in the same “GetInvoiceDiscount” method, in other words, we need to modify the Invoice class. If we are changing the Invoice class again and again then we need to ensure that the previous functionalities along with the new functionalities are working properly by testing both the functionalities again. This is because we need to ensure that the existing client’s, which are referencing to this class is working properly as expected or not.

Open-Closed Principle in C#

As per Open-Closed principle, Instead of “MODIFYING”, we should go for “EXTENSION”.

If you want to follow the Open Closed Principle in the above example, when a new invoice type needs to be added, then we need to add a new class. As a result, the current functionalities that are already implemented are going to be unchanged. The advantage is that we just only need to test and check the new classes.

The following example follows the Open Closed Principle
namespace SOLID_PRINCIPLES.OCP
{
    public class Invoice
    {
        public virtual double GetInvoiceDiscount(double amount)
        {
            double finalAmount = 500;
            return finalAmount;
        }
    }
    public enum InvoiceType
    {
        FinalInvoice,
        ProposedInvoice
    };

    public class FinalInvoice : Invoice
    {
        public override double GetInvoiceDiscount(double amount)
        {
            return base.GetInvoiceDiscount(amount) - 50;
        }
    }

    public class ProposedInvoice : Invoice
    {
        public override double GetInvoiceDiscount(double amount)
        {
            return base.GetInvoiceDiscount(amount) - 40;
        }
    }

    public class RecurringInvoice : Invoice
    {
        public override double GetInvoiceDiscount(double amount)
        {
            return base.GetInvoiceDiscount(amount) - 30;
        }
    }
}

As you can see in the above code, we have created three classes FinalInvoiceProposedInvoice, and RecurringInvoice. All these three classes are inherited from the base class Invoice and if they want then they can override the GetInvoiceDiscount() method. Tomorrow if another Invoice Type needs to be added then we just need to create a new class by inheriting from the Invoice class. The point that you need to keep focus is we are not changing the code of the Invoice class.

Modify the Main method of Program class as shown below to test the application.

namespace SOLID_PRINCIPLES.OCP
{
    class Program
    {
        static void Main(string[] args)
        {
            Invoice FInvoice = new FinalInvoice();
            Invoice PInvoice = new ProposedInvoice();
            Invoice RInvoice = new RecurringInvoice();

            double FInvoiceAmount = FInvoice.GetInvoiceDiscount(10000);
            double PInvoiceAmount = PInvoice.GetInvoiceDiscount(10000);
            double RInvoiceAmount = RInvoice.GetInvoiceDiscount(10000);

            Console.ReadKey();
        }
    }
}

Now, the Invoice class is closed for modification. But it is open for the extension as it allows creating new classes deriving from the Invoice class which clearly follow the Open-Closed Principle in C#. 

In the next article, I am going to discuss the Liskov substitution principle in C# with a real-time example. In this article, I try to explain the Open-Closed Principle in C# with one real-time example. I hope you understood the need and use of the Open-Closed Principle in C#.

2 thoughts on “Open-Closed Principle in C#”

Leave a Reply

Your email address will not be published. Required fields are marked *