Understanding Open Closed Principle (OCP) in C# With Examples

When we write code, it’s important to follow certain rules and principles to make our software easier to work with any update. One of these important SOLID principles is called the Open Closed Principle, or OCP in short.

open closed principle with examples 1
open closed principle in C#

According to Robert C. Martin, also known as Uncle Bob, the definition of the Open Closed Principle (OCP) is as follows:
“Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.”

This definition emphasizes the idea that our code should be designed to allow us to add new features or behaviors without changing the existing code. It encourages using abstractions, interfaces, and polymorphism to enable extension without modification.

By following the Open Closed Principle, we create more flexible, maintainable code and resistance to regression.

In this article, we’ll focus on the Open Closed Principle in C# programming. We’ll learn why it’s important and how it can help us create better software.

What is the Open Closed Principle (OCP)?

The Open Closed Principle (OCP) is a software development principle that tells us we should design our code in a way that allows us to add new features or change existing ones without modifying the existing code. It encourages us to make our code open for extension but closed for modification. By following this principle, we can create software that is easier to maintain, scalable, and adaptable to future changes.

Code Example: Without following the Open Closed Principle

Let’s consider a simple scenario to understand the impact of not following the Open Closed Principle. Suppose we have a Shape class that represents various geometric shapes. 

Initially, the class only supports calculating the area of a rectangle and a circle. However, a new requirement came from a client requesting the addition of a pentagon shape. 

If we are not following the Open Closed Principle, we might be tempted to modify the existing Shape class and add specific logic for calculating the area of a pentagon. This approach violates the Open Closed Principle because it involves directly modifying the existing code.

Here is an example of how the code might look without adhering to the Open Closed Principle:

public class Shape
{
    public double CalculateArea(string shapeType, double[] dimensions)
    {
        if (shapeType == "Rectangle")
        {
            double length = dimensions[0];
            double width = dimensions[1];
            return length * width;
        }
        else if (shapeType == "Circle")
        {
            double radius = dimensions[0];
            return Math.PI * radius * radius;
        }
        else if (shapeType == "Pentagon")
        {
            // Specific logic for calculating the area of a pentagon
            // ...
        }
        else
        {
            throw new ArgumentException("Invalid shape type.");
        }
    }
}

In the above code, the CalculateArea method contains conditional logic that depends on the type of shape. The method becomes cluttered with additional if statements as new shapes are added.

This violates the Open Closed Principle because we end up modifying the existing code to handle new shapes.

Code Example: Following the Open Closed Principle (OCP)

Now let’s refactor the code to follow the Open Closed Principle. We’ll introduce an abstraction using interfaces and leverage polymorphism for extensibility:

using System;
using System.Collections.Generic;

// Abstraction: Shape interface
public interface IShape
{
    double CalculateArea();
}

// Rectangle class implementing IShape
public class Rectangle : IShape
{
    private double length;
    private double width;

    public Rectangle(double length, double width)
    {
        this.length = length;
        this.width = width;
    }

    public double CalculateArea()
    {
        return length * width;
    }
}

// Circle class implementing IShape
public class Circle : IShape
{
    private double radius;

    public Circle(double radius)
    {
        this.radius = radius;
    }

    public double CalculateArea()
    {
        return Math.PI * radius * radius;
    }
}

// Pentagon class implementing IShape
public class Pentagon : IShape
{
    private double sideLength;

    public Pentagon(double sideLength)
    {
        this.sideLength = sideLength;
    }

    public double CalculateArea()
    {
        // Calculate the area of a pentagon using the side length
        double apothem = sideLength / (2 * Math.Tan(Math.PI / 5));
        return (5 * sideLength * apothem) / 2;
    }
}

// Program class for running the console application
class Program
{
    static void Main(string[] args)
    {
        List<IShape> shapes = new List<IShape>();

        // Creating instances of different shapes
        shapes.Add(new Rectangle(5, 10));
        shapes.Add(new Circle(3.5));
        shapes.Add(new Pentagon(4.8));

        // Calculating and displaying the areas of the shapes
        foreach (IShape shape in shapes)
        {
            Console.WriteLine($" Area of {shape.GetType()} = {shape.CalculateArea()}");
        }

        Console.ReadLine();
    }
}

Output:

 Area of Rectangle = 50
 Area of Circle = 38.484510006475
 Area of Pentagon = 39.6397993095698

Code Explanation:

In the above refactored code, we create an interface called IShape that defines a common method, CalculateArea(). Each shape class implements this interface and provides its own implementation of the CalculateArea method.
By leveraging polymorphism, we can now introduce new shapes without modifying the existing code.

For example, to add a pentagon shape, we create a new class Pentagon that implements the IShape interface and provides its own implementation of the area calculation.

How to Implement the Open Closed Principle?

You can use interfaces, abstract classes, abstract methods, and virtual methods to implement the Open Closed Principle. These approaches allow you to extend functionality without changing the existing code by inheriting and implementing them in new classes.

  • Interfaces: Define interfaces that represent behaviors or contracts and have classes implement those interfaces. This allows for adding new implementations without modifying existing code.
  • Abstract Classes: Create abstract classes that provide common functionality and define abstract methods that derived classes must implement. This allows for extending functionality while keeping the core behavior intact.
  • Abstract Methods: Define abstract methods in base classes or interfaces, which derived classes must implement. This enforces the implementation of specific behaviors while allowing for new implementations to be added.
  • Virtual Methods: Use virtual methods in base classes with default implementations that can be overridden in derived classes. It allows for extending or modifying the behavior of a class without changing its existing functionality.

By using these techniques, you can design your code to allow for adding new classes or behaviors without changing the existing code. It ensures that your code follows the Open Closed Principle, which is open for extension but closed for modification.

What are the disadvantages of not following the Open Closed Principle?

When we don’t follow the Open Closed Principle, there are a few drawbacks that we may encounter:

  1. Code Fragility: When we don’t follow the Open Closed Principle, our code becomes fragile and prone to errors. Making changes or adding new features can be risky, as we might accidentally introduce bugs or break existing functionality.
  2. Tight Coupling: Our code can become tightly coupled when we don’t follow the Open Closed Principle. This means that if we make changes to one part of the code, it can affect other parts unexpectedly. So, It becomes difficult for us to work on and modify specific parts of the code without impacting the rest.
  3. Code Duplication: When we don’t design our code to be open for extension, we might copy and repeat code to add new features. This duplication makes it harder to maintain the code.
  4. Difficulty in Testing: Code that doesn’t follow the Open Closed Principle can be challenging to test effectively. Modifications or additions can impact multiple parts of the code, making it difficult to test specific parts independently. As a result, the quality and reliability of our software may be compromised.
  5. Limited Adaptability: Not following the Open Closed Principle can limit the adaptability of our code. When requirements or business needs change, modifying our code to accommodate new features becomes difficult and time-consuming. This makes it challenging to keep up with the evolving demands of the software.

Pros and cons of the Open Closed Principle (OCP)

Here are the pros and cons of the Open Closed Principle (OCP):

Pros:

  • Enhanced Maintainability: The code following Open Closed Principle is easier to maintain. Since existing code is not modified when adding new functionality, there is less risk of introducing bugs or inadvertently affecting other parts of the code. 
  • Scalability and Extensibility: The Open Closed Principle promotes extensibility. By designing code open for extension, new features and behaviors can be added without modifying the existing codebase. This allows for easier scalability and adaptability to accommodate future requirements.
  • Code Reusability: The Open Closed Principle often leads to the creation of abstractions and interfaces that define contracts for extensions. These abstractions can be reused in different contexts, promoting code reusability and reducing duplication. This can save development time and improve code quality.

Cons:

  • Increased Complexity: Following the Open Closed Principle can make the code a bit more complex. We must carefully plan and design abstractions, interfaces, and extension points. It may result in more complex code structures.
  • Higher Initial Development Effort: Implementing the Open Closed Principle may seem more complex and require extra effort in the beginning. It involves designing abstractions, creating extension points, and organizing the code carefully, which takes time and careful thinking. However, the long-term advantages of improved maintainability and extensibility outweigh the initial investment.
  • Dependency on Abstractions: When we follow the Open Closed Principle, we use abstractions and interfaces. However, this can lead to increased complexity and dependencies on these abstractions. Managing and maintaining these dependencies can be challenging. It is important to carefully design our code and adhere to SOLID principles to address this, which can help alleviate these problems.
  • Learning Curve: The Open Closed Principle can add complexity to code and require developers to learn new concepts like object-oriented design, abstraction, and polymorphism. This learning curve may take time, especially for developers who are new to these principles. However, once understood and applied effectively, the Open Closed Principle can greatly enhance the quality and maintainability of code.

FAQs

Here are some frequently asked questions (FAQs) related to the Open Closed Principle (OCP):

Q1: What is the Open Closed Principle?

The Open Closed Principle (OCP) is a software development principle that suggests designing code in a way that allows for adding new features or behaviors without modifying the existing code.

Q2: Why is the Open Closed Principle important? 

The Open Closed Principle enhances code maintainability, scalability, and adaptability. It reduces the risk of introducing bugs and makes code more reusable and extensible.

Q3: How can I implement the Open Closed Principle in my code?

You can implement the Open Closed Principle using interfaces, abstract classes, abstract methods, and virtual methods. These enable the extension of functionality without modifying existing code.

Q4: Does following the Open Closed Principle increase complexity?

Following the Open Closed Principle may introduce some complexity, but the long-term benefits outweigh the initial challenges. It leads to more manageable and flexible code in the long run.

Q5: What are the benefits of following the Open Closed Principle?

Following the Open Closed Principle results in enhanced code maintainability, scalability, reusability, reduced regression risk, and improved software stability.

Recommended Articles:

Shekh Ali
4 1 vote
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments