Introduction

In this lesson, we'll focus on the sprout class technique, but this time in scenarios when we are adding new functionality, and we don't want to disrupt existing code. This approach is particularly useful when we want to expand capabilities while ensuring that the existing behavior remains intact.

Understanding the Need for Sprout Class

As software evolves, new features are often added to meet changing requirements. However, integrating these features into existing classes can inadvertently affect their current functionality. This is where the sprout class technique comes into play. By creating a new class to handle additional responsibilities, we can preserve the original class's behavior. This approach not only maintains the integrity of the existing code but also makes the new functionality easier to manage and test independently.

When integrating new features into existing classes, common problems include increased complexity, reduced readability, and the risk of introducing bugs. The sprout class technique addresses these issues by isolating new behavior in a separate class. This separation of concerns ensures that the original class remains focused on its primary responsibilities, while the new class handles the additional functionality. By doing so, we maintain code clarity and reduce the likelihood of errors.

Implementing Sprout Class

Let's explore how to implement a sprout class. We want to introduce a discount feature in the order processor based on customer loyalty without affecting the existing order processing logic. Here's how we can achieve this:

public class OrderProcessor
{
    public bool ProcessOrder(Order order)
    {
        // ... order processing logic ...
    }
}

In this example, the OrderProcessor class processes orders by calculating the total amount. To introduce a discount feature, we create a new DiscountCalculator class (we sprout it):

public class DiscountCalculator
{
    public decimal CalculateDiscount(Order order)
    {
        // ... discount calculation logic ...
    }
}

The DiscountCalculator class calculates a discount based on the customer's loyalty level.

Next, we introduce another sprout class, the DiscountedOrderProcessor:

public class DiscountedOrderProcessor
{
    private readonly OrderProcessor _orderProcessor;
    private readonly DiscountCalculator _discountCalculator;
    
    public DiscountedOrderProcessor()
    {
        _orderProcessor = new OrderProcessor();
        _discountCalculator = new DiscountCalculator();
    }
    
    public bool ProcessOrderWithDiscount(Order order)
    {
        // ... order processing using both the existing OrderProcessor and the DiscountCalculator ...
    }
}

Using the unchanged logic of the OrderProcessor together with the new discount logic of the DiscountCalculator, we are able to achieve the desired new behavior. While some modifications to the model classes also happen to accommodate the new behavior, we can ensure that they are backwards compatible. The practice sessions following this lesson will demonstrate this.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal