The Open-Closed Principle: A Guide to Writing Maintainable Code

Photo by Kiyota Sage on Unsplash

The Open-Closed Principle: A Guide to Writing Maintainable Code

What Open-Closed Principle (OCP) is?

The Open-Closed Principle is a fundamental principle in software engineering that states that software entities should be open for extension but closed for modification. This principle is one of the core principles of object-oriented design and is essential for creating software that is maintainable, scalable and reusable.

What are the benefits of OCP?

There are several benefits to following the OCP. One of the main benefits is that it makes software easier to maintain. By avoiding modifications to existing code, developers can avoid introducing bugs and can ensure that existing functionality remains intact.

Another benefit is that it makes the software more scalable. By designing software with well-defined interfaces, developers can add new functionality without having to rewrite existing code. This can make it easier to add new features and extend the software over time.

Code Example

Let's dive into an example to make the OCP principle easier to grasp. First up, we'll take a look at some poorly designed code that doesn't follow the OCP principle. Then, we'll see how we can spruce it up to align with the OCP principle.

Suppose we have a game with dragons and want to calculate the attack power of each dragon. Here is an example of what the code might look like:

public class Dragon
{
    public string Type { get; set; }
    public double Size { get; set; }

    public double CalculateAttackPower()
    {
        if (Type == "fire-breathing")
        {
            return Size * 10;
        }
        else if (Type == "ice-breathing")
        {
            return Size * 5;
        }
        else if (Type == "poisonous")
        {
            return Size * 8;
        }
        else
        {
            throw new NotSupportedException();
        }
    }
}

As you can see this code violates the Open-Closed Principle because if we want to add a new type of dragon, such as a lightning-breathing dragon, we would need to modify the existing code by adding another conditional statement to the CalculateAttackPower method. Here is the refactored code that follows the OCP principle:

public abstract class Dragon
{
    public double Size { get; set; }

    public abstract double CalculateAttackPower();
}

public class FireBreathingDragon : Dragon
{
    public override double CalculateAttackPower()
    {
        return Size * 10;
    }
}

public class IceBreathingDragon : Dragon
{
    public override double CalculateAttackPower()
    {
        return Size * 5;
    }
}

public class PoisonousDragon : Dragon
{
    public override double CalculateAttackPower()
    {
        return Size * 8;
    }
}

In this refactored version, we create a base class Dragon and make it abstract. Then, we create subclasses for each type of dragon: FireBreathingDragon, IceBreathingDragon, and PoisonousDragon. Each subclass has its own implementation of the CalculateAttackPower method, which overrides the abstract method in the base class. This adheres to the "open for extension" principle of the OCP because if we want to add a new type of dragon, we can simply create a new subclass without modifying the existing code.

Did you find this article valuable?

Support Theodoros Karropoulos by becoming a sponsor. Any amount is appreciated!