← All categories

Design Principles

The rules of thumb that keep classes small, focused and easy to change.

Principles such as single responsibility, open/closed, Liskov substitution, interface segregation and dependency inversion are not patterns you add; they are properties you check. Code that breaks them still runs, but every change costs more than it should.

Problems in this category ask you to find the principle being broken, usually through a concrete symptom: a class that changes for four unrelated reasons, a subclass that throws on half its methods, or business logic that can't be tested without a real database.

You'll recognise it when

  • One class changes whenever pricing, layout or storage changes.
  • A subclass overrides methods only to throw an exception.
  • Business rules create their own database or HTTP clients.
  • Callers read an object's data, decide, and write it back.

Example

A weather station sends alerts when rain is likely.

Before
class RainAlert {
    void check(Forecast f) {
        if (f.rainChance() > 70) {
            TwilioClient sms = new TwilioClient("AC9f…", "secret");
            sms.send("+919800000000", "Carry an umbrella");
        }
    }
}
After
interface AlertChannel {
    void send(String message);
}

class RainAlert {
    private final AlertChannel channel;
    RainAlert(AlertChannel channel) { this.channel = channel; }

    void check(Forecast f) {
        if (f.rainChance() > 70) channel.send("Carry an umbrella");
    }
}

Why it's better: The rule (when to alert) no longer depends on how alerts are delivered. It can be tested with a fake channel, and SMS, email or push are plug-ins rather than edits.

Ideas you'll practise

Single ResponsibilityOpen/ClosedLiskov SubstitutionInterface SegregationDependency InversionComposition over inheritanceLaw of DemeterTell, don't askDRY, KISS, YAGNI

Problems 17