Skip to main content
CodeOath
← All posts

Architecture & Patterns110 min total · 26 parts

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

Part 16 of 26 · ~2 min

Structural Patterns: Adapter and Facade

This next family is about assembly rather than construction — how objects that already exist get combined into something bigger, with none of the pieces themselves needing an edit to take part.

Adapter sits between two interfaces that were never designed to match, and makes one look like the other from the caller's point of view, so code on both sides keeps working exactly as written, unmodified, unaware anything was bridged at all:

// The interface ShiftDesk's payroll-reporting code already expects everywhere.
public interface IHoursTracker
{
    void RecordHours(string volunteerId, TimeSpan hours);
}

// A vendor's legacy paper-timesheet scanner, with an interface you don't control.
public class OldTimesheetScanner
{
    public int ScanAndTally(string batchId) => 0; // returns minutes as a bare int
}

// The adapter translates between the two, without modifying either.
public class LegacyTimesheetAdapter : IHoursTracker
{
    private readonly OldTimesheetScanner _scanner;
    public LegacyTimesheetAdapter(OldTimesheetScanner scanner) => _scanner = scanner;
    public void RecordHours(string volunteerId, TimeSpan hours) { /* not the shape this adapter uses */ }
    public void RecordFromBatch(string volunteerId, string batchId) =>
        RecordHours(volunteerId, TimeSpan.FromMinutes(_scanner.ScanAndTally(batchId)));
}

Facade collapses several classes' worth of coordination into one easy entry point, so nobody calling in has to memorize the correct sequence across all of them just to get a routine thing done. Checking a volunteer in at the shelter's kiosk actually touches four separate subsystems behind the scenes — print a badge, log the attendance, decrement the day's walk-in capacity, and ping the coordinator's tablet:

// Without a facade, every kiosk screen needs to know the correct order of
// operations across four separate subsystems, and get all four right.
public class VolunteerCheckInFacade
{
    private readonly IBadgePrinter _printer;
    private readonly IAttendanceLog _log;
    private readonly ICapacityCounter _capacity;
    private readonly IShiftNotifier _notifier;

    public VolunteerCheckInFacade(IBadgePrinter printer, IAttendanceLog log, ICapacityCounter capacity, IShiftNotifier notifier)
    {
        _printer = printer; _log = log; _capacity = capacity; _notifier = notifier;
    }

    public void CheckIn(string volunteerId, int shiftId)
    {
        _printer.Print(volunteerId);
        _log.Record(volunteerId, shiftId, DateTime.UtcNow);
        _capacity.DecrementWalkIn(shiftId);
        _notifier.Send($"{volunteerId} just checked in for shift {shiftId}.");
    }
}

// One simple call is all any kiosk screen needs to know about.
checkInFacade.CheckIn("v_2291", shiftId: 88);

What actually separates these two patterns is the problem each one solves, not how the code happens to be shaped. Reach for Adapter when the obstacle is a mismatch — two interfaces that each work fine on their own but were never built to fit together. Reach for Facade when there's no mismatch at all, just too many correct steps for a caller to remember and sequence correctly by itself.