Course contentsShow
← C#
Lesson 252 of 1,9082. Object-Oriented Design in C#Free lesson

Managing Feature Evolution

You will extend core business models with new features without creating combinatorial class explosions.

In this lesson, you will learn how to extend your core business models with new features dynamically without falling into the trap of "combinatorial class explosion."

The Problem: Class Explosion

Imagine you have a Product class. You want to add Taxable, Discountable, and Shippable features. If you create a class for every possible combination (e.g., TaxableDiscountableProduct, TaxableShippableProduct, etc.), your codebase will quickly become impossible to maintain. This is "combinatorial explosion"—where the number of classes grows exponentially with every new feature.

The Solution: Composition over Inheritance

Instead of building rigid hierarchies, we use Composition. Instead of baking features into the Product type, we treat features as independent components that can be "attached" to a core model at runtime.

By using an interface-based approach combined with the Decorator or Strategy pattern, we can evolve our features independently.

public interface IProduct {
    decimal GetPrice();
}

// The core model remains clean
public class BaseProduct : IProduct {
    public decimal GetPrice() => 100m;
}

// Features are separate classes
public class TaxDecorator : IProduct {
    private readonly IProduct _inner;
    public TaxDecorator(IProduct inner) => _inner = inner;
    public decimal GetPrice() => _inner.GetPrice() * 1.1m;
}

Why this works for Evolution

When a new requirement arrives (e.g., "Holiday Promotion"), you simply create a HolidayDecorator class. You don't need to touch BaseProduct or create new, complex inheritance branches.

This approach aligns perfectly with our previous work on Middleware Pipelines and Context-Aware Dependency Injection. You can resolve the base component and then "wrap" it with the necessary feature-specific decorators based on the current context or configuration. This keeps your core logic stable while allowing the object's capabilities to evolve fluidly as user needs change. You move away from asking, "What is this object?" and start asking, "What behaviors can this object currently perform?"

Key Takeaway: Use composition and decorators to layer features onto your domain models, keeping your core classes simple and preventing the creation of rigid, unmanageable class hierarchies.