Design Patterns SOLID Principles

SOLID is a set of five guidelines that keep object-oriented code clean, flexible, and easy to change. Robert C. Martin popularized them, and nearly every design pattern follows at least one. Learning the five rules first explains why the patterns look the way they do.

The Five Letters at a Glance

┌───┬──────────────────────────────┬─────────────────────────────┐
│ S │ Single Responsibility        │ One class, one job          │
│ O │ Open / Closed                │ Extend without editing      │
│ L │ Liskov Substitution          │ Children replace parents    │
│ I │ Interface Segregation        │ Small, focused interfaces   │
│ D │ Dependency Inversion         │ Depend on abstractions      │
└───┴──────────────────────────────┴─────────────────────────────┘

S: Single Responsibility Principle

A class should have one reason to change. Picture a restaurant where one person cooks, serves, and handles the cash. A busy night breaks that person. Separate kitchen roles keep the work smooth.

Bad:   ┌─────────────────────────┐
       │ Report                  │
       │ calculateTotals()       │
       │ printToPaper()          │   three jobs in one class
       │ saveToDatabase()        │
       └─────────────────────────┘

Good:  Report        ReportPrinter       ReportStorage
       calculate()   print()             save()

O: Open/Closed Principle

Code should welcome new features without editing old, tested code. A power strip accepts new plugs without rewiring the wall. Strategy and Decorator show this rule at work: you add a new class and leave the existing ones alone.

// Closed to change, open to extension
interface Discount { double apply(double price); }

class FestivalDiscount implements Discount { ... }
class MemberDiscount   implements Discount { ... }   // new class, no edits elsewhere

L: Liskov Substitution Principle

Wherever a program uses a parent class, a child class must fit in without surprises. A toy duck that quacks can replace a real duck in a quacking contest. Picture a rubber duck that refuses to quack: it breaks the contest, and any design forcing it to inherit from Duck needs repair.

Warning Sign

A child class that throws an error for a parent method signals a broken promise. Rework the hierarchy or choose composition.

I: Interface Segregation Principle

Small interfaces beat one giant interface. A basic printer should not need to implement scan() and fax() just because a large Machine interface lists them.

Bad:   Machine { print(); scan(); fax(); }

Good:  Printer { print(); }
       Scanner { scan(); }
       FaxUnit { fax(); }

BasicPrinter     implements Printer
OfficeAllInOne   implements Printer, Scanner, FaxUnit

D: Dependency Inversion Principle

High-level code should depend on abstractions instead of concrete classes. A lamp works with any wall socket because the socket shape is a standard. The lamp never cares which power plant feeds the grid.

Before:   OrderService ─────────▶ MySqlDatabase      (tight link)

After:    OrderService ─────────▶ Database (interface)
                                      ▲
                          ┌───────────┴──────────┐
                     MySqlDatabase         FileDatabase

Dependency Injection, covered later in this course, puts this principle into daily practice.

Principles Behind Popular Patterns

PatternMain SOLID Rule It Supports
StrategyOpen/Closed
DecoratorOpen/Closed
FacadeSingle Responsibility
AdapterDependency Inversion
Factory MethodDependency Inversion

Common Mistakes

  • Splitting classes into tiny pieces until nobody can follow the code.
  • Adding interfaces for objects that will never change.
  • Treating the five rules as laws instead of guidelines.

Good judgment matters more than rigid rule-following. Apply each principle where change is likely.

Applying SOLID Step by Step

Begin with a class that feels crowded. Count its reasons to change: a tax rule change, a new report layout, a new storage system. Three reasons point to three responsibilities, and each deserves a class. Extract one responsibility at a time and run the tests after each move.

Inspect the dependencies after the split. Replace concrete class names with interfaces wherever the implementation may vary. Leave stable helpers alone, because an interface for a class that never changes adds noise without value.

Practice Idea

Pick a small program you wrote, such as a to-do list. Write down which class breaks each of the five rules. Fix the most painful one, and compare the before and after code. Real progress comes from small, repeated cleanups.

Key Takeaways

  • SOLID stands for Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion.
  • Each rule reduces the cost of future changes.
  • Design patterns offer ready-made ways to follow these rules.