SOLID principles — cheat sheet
General OOP-design knowledge, no course slide source — written directly, same as 10-dependency-injection.md. Ties heavily into material already covered here, cross-linked below.
S — Single Responsibility Principle
A class should have exactly one reason to change — one responsibility.
Already covered in depth: 01-data-and-service-classes.md (the EverythingDoer vs. FileDownloader/TemperatureConverter/VowelCounter example).
O — Open/Closed Principle
Classes should be open for extension, but closed for modification — you should be able to add new behavior without editing existing, already-tested code.
Achieved via polymorphism: instead of a method with a growing if/else/switch chain over types (if (shape instanceof Circle) ... else if (shape instanceof Square) ...), define a Shape interface with an area() method, and let each shape class implement its own. Adding a new shape means adding a new class, not editing the existing area-calculation code (and not risking breaking the shapes that already worked).
L — Liskov Substitution Principle
A subclass should be usable anywhere its superclass/interface is expected, without breaking the caller's expectations — substituting a subtype in should never produce surprising or incorrect behavior.
Classic broken example: Square extends Rectangle, overriding setWidth/setHeight so that setting one also changes the other (to keep it a square). Code that does rectangle.setWidth(5); rectangle.setHeight(10); assert rectangle.area() == 50; now silently breaks when handed a Square — the subclass violated an assumption (width/height are independent) that the superclass's contract implied.
Ties to 11-inheritance-and-abstract-classes.md: this is the principle that decides whether an "is-a" relationship is actually appropriate for inheritance, not just superficially true.
I — Interface Segregation Principle
Prefer several small, specific interfaces over one large, general-purpose one — a class shouldn't be forced to implement methods it doesn't need just because they're bundled into an interface it otherwise wants.
E.g. instead of one bloated Worker interface with code(), test(), deploy(), makeCoffee(), split it into focused interfaces (Coder, Tester, Deployer) so a class only implements what actually applies to it, instead of stubbing out methods with empty bodies or throw new UnsupportedOperationException().
D — Dependency Inversion Principle
High-level modules shouldn't depend on low-level modules directly — both should depend on abstractions (interfaces). And abstractions shouldn't depend on details; details should depend on abstractions.
This is exactly the pattern already worked through in 10-dependency-injection.md: DamageCalculator (high-level) ends up depending on the RandomNumberGenerator interface (an abstraction), not on SimpleRandomNumberGenerator (a low-level detail) — so the detail can change (or be swapped for a mock) without the high-level class changing at all. It's also exactly why Spring services get constructor-injected with interface types rather than concrete classes.
Interview framing
You're unlikely to be asked to recite the acronym cold — more likely you'll be shown a small code smell and asked "which SOLID principle does this violate, and how would you fix it." Being able to point at the actual project examples above (the DI exercise for D, the data/service class split for S) is stronger than reciting definitions.