Skip to content

Expert · Design patterns, JVM internals and interviews · topic 56 of 60 · 30 min

SOLID principles

Explanation

SOLID is five guidelines for object-oriented design: Single responsibility (one reason to change), Open/closed (extend without editing), Liskov substitution (subclasses must keep the parent's promises), Interface segregation (small focused interfaces) and Dependency inversion (depend on abstractions, not concrete classes). They keep large codebases changeable.

Why principles?

Code that works today must change tomorrow: new payment methods, new tax rules, new report formats. Badly structured code makes every change risky. SOLID, named by Robert C. Martin, summarises habits that make change cheaper. Treat them as guidelines, not laws.

S: Single responsibility

java
// Before: one class with three reasons to change
class InvoiceService {
    double total(Invoice i) { … }          // tax rules change
    byte[] toPdf(Invoice i) { … }          // PDF layout changes
    void email(Invoice i) { … }            // email provider changes
}
// After: one job each
class InvoiceCalculator { double total(Invoice i) { … } }
class InvoicePdfRenderer { byte[] render(Invoice i) { … } }
class InvoiceMailer { void send(Invoice i, byte[] pdf) { … } }

O: Open/closed

Classes should be open for extension but closed for modification: add new behaviour by adding code, not by editing a growing if/else chain. Polymorphism is the usual tool.

java
interface DiscountRule { double apply(double amount); }
class FestivalDiscount implements DiscountRule { public double apply(double a) { return a * 0.9; } }
class MemberDiscount implements DiscountRule { public double apply(double a) { return a > 500 ? a - 50 : a; } }
// A new rule = a new class; the checkout code that loops over List<DiscountRule> never changes.

L: Liskov substitution

Anywhere the code expects a parent type, any subclass must work without surprises. The classic violation: Square extends Rectangle. Setting a Rectangle's width should not change its height, but a Square must keep both equal, so code that works with rectangles breaks with squares. If a subclass has to throw UnsupportedOperationException for an inherited method, the inheritance is probably wrong.

I: Interface segregation

java
// Too big: a simple printer must implement scan and fax
interface OfficeMachine { void print(Doc d); void scan(Doc d); void fax(Doc d); }
// Better: small, focused interfaces
interface Printer { void print(Doc d); }
interface Scanner { void scan(Doc d); }
class BasicPrinter implements Printer { public void print(Doc d) { … } }

D: Dependency inversion

High-level code (OrderService) should depend on abstractions (a PaymentGateway interface), not on concrete details (RazorpayGateway). The details implement the abstraction. Spring's dependency injection (module 11) is built around this principle.

Careful: Applying every principle everywhere produces "over-engineering": ten interfaces with one implementation each, factories for simple objects, and code nobody can follow. Start simple; introduce an interface or split a class when a real second variation or a real change appears.
Think of it like this: SOLID is like the rules for a well-run kitchen: each cook has one station (S); a new dish adds a new recipe card rather than rewriting old ones (O); any trained chef can replace another at a station (L); each cook gets only the tools for their station (I); and cooks follow recipes (abstractions), not one supplier's specific packet instructions (D).
Tip: In interviews, give one concrete before/after example per letter from your own projects. Examples convince far more than definitions.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

SOLID principles · 1/6

SOLID

In this lesson you will learn the five SOLID principles of object-oriented design.

Prefer a video by a teacher? Find free videos on “solid principles java examples explained” on YouTube ↗

In real life: Adding a new tax rule without touching checkout

An e-commerce company had GST logic inside a 600-line checkout method. Every tax change risked breaking payments. Refactoring into a TaxRule interface with one class per rule meant the next GST change was a new 20-line class and its tests, with the checkout code untouched.

java
interface TaxRule { boolean appliesTo(Item i); long taxPaise(Item i); }
long totalTax(List<Item> items, List<TaxRule> rules) {
    return items.stream()
            .mapToLong(i -> rules.stream().filter(r -> r.appliesTo(i)).mapToLong(r -> r.taxPaise(i)).sum())
            .sum();
}

Practice (0/3 done)

Task 1 · Question

class ReportService { load data; calculate totals; format as Excel; email it } violates mainly…

Task 2 · Question

A ReadOnlyList extends ArrayList and throws UnsupportedOperationException from add(). Which principle is broken?

Task 3 · Practice

Take a method that calculates shipping with if/else on the courier name (if "BlueDart" … else if "Delhivery" … else if "IndiaPost" …). Refactor it with an interface ShippingRate and one class per courier, so adding a new courier needs no change to existing code. Name which SOLID principles your refactor applies.

Quick quiz

Question 1

1.What does the O in SOLID stand for?

Question 2

2.Dependency inversion says high-level code should depend on…

Question 3

3.What is a risk of applying SOLID everywhere from day one?

Question 4

4.Interface segregation recommends…

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does the O in SOLID stand for?

Answer: Open/closed

Open for extension, closed for modification.

Dependency inversion says high-level code should depend on…

Answer: Abstractions (interfaces)

Details implement the abstractions.

What is a risk of applying SOLID everywhere from day one?

Answer: Over-engineering with needless interfaces and layers

Add abstractions when real variation appears.

Interface segregation recommends…

Answer: Small, focused interfaces

Clients should not depend on methods they do not use.

How many classes can a Kotlin class extend?Related

Answer: One

One superclass, but many interfaces.

From Inheritance and interfaces

How many classes can a Dart class extend?Related

Answer: One

Single inheritance; many interfaces and mixins.

From Inheritance, interfaces, mixins and extensions

Which annotation creates a bean from a method in a configuration class?Related

Answer: @Bean

@Bean methods inside @Configuration classes.

From Spring and dependency injection

How many classes can a Java class directly extend?Related

Answer: One

Java has single inheritance for classes.

From Inheritance with extends

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Design patterns, JVM internals and interviews” before its exam: Common design patterns, JVM memory and garbage collection, Java interview questions, Projects and a Java career.
  2. Module 12 exam: Design patterns, JVM internals and interviews · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. 60 topics left in the course before the final exam.

← Previous: JPA and Spring Data repositories