DEV Community

Cover image for Understanding the Open/Closed Principle (OCP) from SOLID: Keep Code Flexible Yet Stable
Daniel Azevedo
Daniel Azevedo

Posted on

Understanding the Open/Closed Principle (OCP) from SOLID: Keep Code Flexible Yet Stable

When we talk about writing clean, maintainable, and scalable code, one of the first principles that comes to mind is the Open/Closed Principle (OCP) from the SOLID principles. It’s a concept that encourages developers to design systems that are flexible enough for future changes while minimizing the impact on existing functionality.

But how does it work in practice?

What is the Open/Closed Principle?

The Open/Closed Principle states that:

“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

In simple terms, this means that your code should be structured in a way that allows new functionality to be added without altering existing code. Why is this so important? It helps prevent bugs or issues from being introduced when making changes, especially in large codebases.

Real-World Example: Payroll System

Imagine you have a payroll system that calculates salaries for employees. You start by calculating a basic salary. Now, what happens when a new requirement comes in to calculate bonuses or commissions? Instead of modifying the core logic of your salary calculation (which could lead to bugs), you could apply the OCP principle to extend the functionality.

Here’s an example in C# to demonstrate this:

// Base class for salary calculation
public abstract class SalaryCalculator
{
    public abstract decimal CalculateSalary(decimal baseSalary);
}

// Extension for basic salary calculation
public class BasicSalaryCalculator : SalaryCalculator
{
    public override decimal CalculateSalary(decimal baseSalary)
    {
        return baseSalary;
    }
}

// Extension for bonus salary calculation
public class BonusSalaryCalculator : SalaryCalculator
{
    private readonly SalaryCalculator _baseSalaryCalculator;
    private readonly decimal _bonus;

    public BonusSalaryCalculator(SalaryCalculator baseSalaryCalculator, decimal bonus)
    {
        _baseSalaryCalculator = baseSalaryCalculator;
        _bonus = bonus;
    }

    public override decimal CalculateSalary(decimal baseSalary)
    {
        return _baseSalaryCalculator.CalculateSalary(baseSalary) + _bonus;
    }
}

// Usage example
var basicSalary = new BasicSalaryCalculator();
var bonusSalary = new BonusSalaryCalculator(basicSalary, 500);

Console.WriteLine($"Basic Salary: {basicSalary.CalculateSalary(3000)}"); // 3000
Console.WriteLine($"Salary with Bonus: {bonusSalary.CalculateSalary(3000)}"); // 3500
Enter fullscreen mode Exit fullscreen mode

In this example, we’ve applied OCP by extending the salary calculator with a bonus feature without modifying the existing logic. This way, if more features come in (like taxes, overtime, etc.), we can simply extend the functionality through new classes rather than touching the existing ones.

Why Does OCP Matter?

  • Flexibility: It allows systems to grow without affecting existing functionality.
  • Maintainability: Makes your code easier to understand and modify.
  • Scalability: Encourages systems that can handle new requirements seamlessly.

Potential Pitfalls

While OCP is incredibly powerful, it’s important not to over-engineer a solution. If you apply too many layers of abstraction too soon, you could end up with a complex codebase that's hard to follow. So, always consider the current and foreseeable needs of the application when deciding how to apply OCP.

Conclusion

The Open/Closed Principle helps us build flexible and robust systems that can evolve with changing requirements. By keeping classes open for extension but closed for modification, we can write code that’s easier to maintain, extend, and scale over time.

What are your thoughts on applying OCP? Have you found it challenging to implement in certain scenarios? Let’s discuss in the comments!

Top comments (0)