SOLID - Open/Closed Principle in C#

The “O” in SOLID stands for the Open/Closed Principle (OCP), which states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. In other words, you should be able to extend the behavior of a system without modifying its existing code.

Example: Violation of the Open/Closed Principle

Suppose we have a class FastFoodOrder that calculates the total cost of an order based on the type of food items ordered (burgers and tacos in this case). To demonstrate a violation of the Open/Closed Principle, we will modify the class each time we need to add a new type of food item.

public class FastFoodOrder
{
    public enum FoodType
    {
        Burger,
        Taco
    }

    public decimal CalculateTotalOrderCost(List<FoodType> itemsOrdered)
    {
        decimal totalCost = 0m;
        foreach (var item in itemsOrdered)
        {
            switch (item)
            {
                case FoodType.Burger:
                    totalCost += 5.00m; // Assume each burger costs $5.00
                    break;
                case FoodType.Taco:
                    totalCost += 3.00m; // Assume each taco costs $3.00
                    break;
                // If we want to add a new food type, we have to modify this method.
            }
        }
        return totalCost;
    }
}

This design violates the Open/Closed Principle because if we want to add a new type of food item to our fast-food ordering system (e.g., a salad or a soft drink), we have to modify the CalculateTotalOrderCost method by adding a new case to the switch statement. This means we are not extending the existing functionality; instead, we are modifying the existing code, which could introduce bugs into a previously working system and violates the principle of being closed for modification.

Embracing Open/Closed: A Strategy for Refactoring

To align with OCP, we refactor our example using polymorphism, employing an interface to represent general food items and implementing concrete classes for specific items. This strategy facilitates the addition of new food types without altering existing code.

Step 1: Establish a Food Item Interface

public interface IFoodItem
{
    decimal GetPrice();
}

Step 2: Implement Concrete Food Item Classes

public class Burger : IFoodItem
{
    public decimal GetPrice() => 5.00m;
}

public class Taco : IFoodItem
{
    public decimal GetPrice() => 3.00m;
}

// Extending the system with a new item is straightforward
public class Salad : IFoodItem
{
    public decimal GetPrice() => 4.00m;
}

Step 3: Update the Order Calculation Mechanism

public class FastFoodOrder
{
    public decimal CalculateTotalOrderCost(List<IFoodItem> itemsOrdered)
    {
        return itemsOrdered.Sum(item => item.GetPrice());
    }
}

Adherence to OCP and Its Benefits

With this refactoring, the FastFoodOrder class becomes extensible without necessitating modifications to its core logic. Adding a new menu item, like a soft drink, involves simply implementing the IFoodItem interface:

public class SoftDrink : IFoodItem
{
    public decimal GetPrice()
    {
        return 2.00m; // Price of a soft drink
    }
}

This approach not only adheres to the Open/Closed Principle but also enhances code robustness by segregating extensible behavior into separate classes, thereby reducing the risk of bugs during expansion.

Wrapping Up

Adopting the Open/Closed Principle is important for developing scalable and maintainable software. By designing systems that are open for extension but closed for modification, developers can accommodate future changes with minimal impact on existing code. While the principle may not always be practical in the early stages of development or for simple applications, its disciplined application is a hallmark of sophisticated software engineering.

Recent Posts

SOLID - Dependency Inversion Principle (DIP) in C#
SOLID - Interface Segregation Principle (ISP) in C#
SOLID - Liskov Substitution Principle (LSP) in C#
SOLID - Open/Closed Principle (OCP) in C#
SOLID - Single Responsiblity Principle (SRP) in C#
How to Make a CLI Menu in C#