Lesson 2 of 45 intermediate

SOLID: Five Rules That Keep Code Changeable

Why adding a feature should mean adding code, not editing five old files

Guess first

Pick an answer. You will see if you were right at the end of the lesson.

OrderService has a switch on discount type. A new discount type arrives. What does Open/Closed suggest?

Open interactive version (quiz + challenge)

Real-world analogy

Think of a kitchen with one cook who also washes dishes, takes orders and does the books. When the menu changes, that one person must change everything, and any mistake spreads. A good kitchen gives each job to one person and lets you hire a new cook without rewriting the others. Here each cook is a class, and SOLID is the set of rules for splitting the jobs.

What is it?

SOLID is five design rules for object-oriented code: Single responsibility, Open/Closed, Liskov substitution, Interface segregation and Dependency inversion. They all aim at one thing: a change in one place should not force changes in many places. They are guidance learned from painful codebases, not a checklist to apply everywhere.

Real-world relevance

A shop starts with one discount and a switch in OrderService. A year later there are twelve discount types and every new one risks breaking the old ones. Moving each discount into its own IDiscountRule class means a new promotion is one new file, with its own tests.

Key points

Quick check

Answer from memory — scrolling up first makes it stick less.

A subclass overrides a base method and throws NotSupportedException. Which principle is broken?

What does the Single Responsibility Principle really ask?

Code example

public record Customer(bool IsVip);
public record Order(Customer Customer, List<string> Items, decimal Total);

public interface IDiscountRule
{
    bool AppliesTo(Order order);
    decimal Discount(Order order);
}

public class VipDiscount : IDiscountRule
{
    public bool AppliesTo(Order o) => o.Customer.IsVip;
    public decimal Discount(Order o) => o.Total * 0.10m;
}

public class BulkDiscount : IDiscountRule
{
    public bool AppliesTo(Order o) => o.Items.Count >= 10;
    public decimal Discount(Order o) => 5m;
}

public class OrderService
{
    private readonly IEnumerable<IDiscountRule> _rules;

    public OrderService(IEnumerable<IDiscountRule> rules) => _rules = rules;

    public decimal FinalTotal(Order order)
    {
        var discount = _rules
            .Where(rule => rule.AppliesTo(order))
            .Sum(rule => rule.Discount(order));
        return order.Total - discount;
    }
}

Line-by-line walkthrough

  1. 1. IDiscountRule is a small contract: can I apply to this order, and how much do I take off.
  2. 2. VipDiscount and BulkDiscount each hold one rule. Each has one reason to change.
  3. 3. OrderService asks for IEnumerable of IDiscountRule in its constructor. It does not know which rules exist.
  4. 4. FinalTotal keeps only the rules that apply, sums their discounts, and subtracts the result.
  5. 5. A new seasonal discount is a new class added to the list. OrderService stays the same.

Spot the bug

public abstract class Bird
{
    public abstract void Fly();
}

public class Penguin : Bird
{
    public override void Fly()
    {
        throw new NotSupportedException("Penguins cannot fly.");
    }
}
Need a hint?
Can code that accepts a Bird safely call Fly on every Bird?
Show answer
This breaks Liskov Substitution. Code that takes a Bird and calls Fly will crash when it gets a Penguin. Fix: do not put Fly on the base type. Split the contract, for example a Bird base and an <code>IFlyingBird</code> interface only for birds that fly.

Explain like I'm 5

Give each helper in your house one job, so if the dishwasher breaks you do not also lose the oven. When you want a new helper, you add one without moving the old ones.

Fun fact

The five principles were collected by Robert C. Martin in the early 2000s, and the name SOLID came from Michael Feathers. Barbara Liskov, whose name is on the L, described the idea in 1987.

Hands-on challenge

Say it out loud in 30 seconds: "Explain the Open/Closed Principle with a real example, and say when SOLID is overkill." Then check: did you mention adding a class instead of an if, a list of rules, and small or stable code?

More resources

Open interactive version (quiz + challenge) Download the whole course as PDF / Word

Lock it in

← Back to course: .NET + React Playbook