Skip to main content
CodeOath
← All posts

Architecture & Patterns110 min total · 26 parts

ACID, SOLID, and Design Patterns: A Complete Software Design Reference

Part 19 of 26 · ~1 min

Behavioral Patterns: Strategy

This last group is about who talks to whom, and how the job of deciding what happens next gets divided up between objects while the program is running. Strategy is the simplest case: a shared interface behind which several interchangeable algorithms live, with the choice of which one is active made — and changeable — at runtime, rather than baked into a long if/switch chain. You already watched this exact fix land once, back in the Open/Closed chapter.

public interface IMatchingStrategy
{
    Volunteer? Match(Shift openShift, List<Volunteer> candidates);
}

public class NearestSiteMatching : IMatchingStrategy
{
    public Volunteer? Match(Shift openShift, List<Volunteer> candidates) =>
        candidates.OrderBy(v => v.DistanceToSite(openShift.SiteId)).FirstOrDefault();
}

public class FairRotationMatching : IMatchingStrategy
{
    public Volunteer? Match(Shift openShift, List<Volunteer> candidates) =>
        candidates.OrderBy(v => v.ShiftsThisMonth).FirstOrDefault();
}

public class ShiftMatcher
{
    private readonly IMatchingStrategy _strategy;
    public ShiftMatcher(IMatchingStrategy strategy) => _strategy = strategy;
    public Volunteer? FindMatch(Shift openShift, List<Volunteer> candidates) => _strategy.Match(openShift, candidates);
}

// The active strategy is chosen at runtime and handed in — ShiftMatcher itself never changes.
var matcher = new ShiftMatcher(new FairRotationMatching());

Same shape as IPriorityRule from the OCP chapter, on purpose — once you've built the interface-instead-of-branches shape once in a codebase, every subsequent "which variant should run here" question tends to reach for the same answer.