Sunday, 26 July 2026

SOLID principles with Design Patterns

 

SOLID + Design Patterns (Java Examples)

One of the most common Java interview questions is:

Which Design Patterns follow which SOLID Principle?

The answer is that design patterns are practical implementations of SOLID principles.


SOLID vs Design Patterns

SOLID PrincipleDesign PatternsWhy?
SRPFacade, DAO, Repository, Service LayerOne class = One responsibility
OCPStrategy, Decorator, Template MethodAdd new behavior without changing existing code
LSPFactory Method, Template MethodChild classes can replace parent classes
ISPAdapter, BridgeSmall focused interfaces
DIPFactory, Abstract Factory, Dependency Injection, BuilderDepend on interfaces, not implementations

1. SRP + DAO Pattern

❌ Bad Design

class Employee {

    void calculateSalary(){}

    void saveEmployee(){}

    void sendEmail(){}
}

Three responsibilities.


✅ Good Design

class EmployeeService {

    void calculateSalary(){}
}
class EmployeeRepository {

    void save(Employee e){}
}
class EmailService {

    void sendMail(){}
}

Pattern Used

Controller
      |
      V
Service
      |
      V
Repository (DAO)

Every class has one responsibility.


2. OCP + Strategy Pattern

Suppose Flipkart offers different payment methods.

Instead of:

if(payment.equals("UPI"))

if(payment.equals("CARD"))

if(payment.equals("NETBANKING"))

Use Strategy.


Strategy Interface

interface PaymentStrategy {

    void pay(double amount);

}

UPI Strategy

class UpiPayment implements PaymentStrategy {

    public void pay(double amount){

        System.out.println("UPI Payment");

    }

}

Card Strategy

class CardPayment implements PaymentStrategy {

    public void pay(double amount){

        System.out.println("Card Payment");

    }

}

Client

class PaymentService {

    private PaymentStrategy payment;

    PaymentService(PaymentStrategy payment){

        this.payment = payment;

    }

    void checkout(){

        payment.pay(1000);

    }

}

Adding Wallet Payment?

Just create

class WalletPayment implements PaymentStrategy{}

No existing code changes.

OCP achieved.


3. OCP + Decorator Pattern

Imagine Coffee Shop.

Base Coffee

interface Coffee {

    String getDescription();

}

Simple Coffee

class SimpleCoffee implements Coffee {

    public String getDescription(){

        return "Coffee";

    }

}

Decorator

class MilkDecorator implements Coffee{

    private Coffee coffee;

    MilkDecorator(Coffee coffee){

        this.coffee=coffee;

    }

    public String getDescription(){

        return coffee.getDescription()+" + Milk";

    }

}

Usage

Coffee coffee = new MilkDecorator(
                    new SimpleCoffee());

System.out.println(coffee.getDescription());

Output

Coffee + Milk

Want Sugar?

Create

SugarDecorator

No modification.

Only extension.


4. LSP + Factory Method

Vehicle example

abstract class Vehicle{

    abstract void start();

}
class Car extends Vehicle{

    void start(){

        System.out.println("Car Started");

    }

}
class Bike extends Vehicle{

    void start(){

        System.out.println("Bike Started");

    }

}

Client

Vehicle vehicle = VehicleFactory.create("CAR");

vehicle.start();

Client never worries whether it is Car or Bike.

Any subclass works.

LSP satisfied.


5. ISP + Adapter Pattern

Suppose Printer

Bad Interface

interface Printer{

    print();

    scan();

    fax();

}

Old Printer

Needs only print.

Forced to implement scan().

Violation.


Split Interfaces

interface Printable{

    void print();

}
interface Scannable{

    void scan();

}

Adapter

class OldPrinterAdapter implements Printable{

    OldPrinter printer;

    public void print(){

        printer.print();

    }

}

Only required methods implemented.


6. DIP + Factory Pattern

Bad

class NotificationService{

    EmailSender sender=new EmailSender();

}

Tightly coupled.


Better

interface MessageSender{

    void send();

}
class EmailSender implements MessageSender{}
class SmsSender implements MessageSender{}

Factory

class SenderFactory{

    static MessageSender getSender(String type){

        if(type.equals("EMAIL"))
            return new EmailSender();

        return new SmsSender();

    }

}

Client

MessageSender sender =
        SenderFactory.getSender("EMAIL");

sender.send();

NotificationService depends only on

MessageSender

DIP achieved.


7. DIP + Spring Dependency Injection ⭐

Without Spring

class OrderService{

    MySqlRepository repo =
            new MySqlRepository();

}

Coupled.


With Spring

interface OrderRepository{

    void save();

}
@Repository
class MySqlRepository
implements OrderRepository{

}
@Service
class OrderService{

    private final OrderRepository repo;

    OrderService(OrderRepository repo){

        this.repo=repo;

    }

}

Spring injects implementation automatically.

This is Dependency Inversion in action.


8. SRP + Builder Pattern

Instead of

Employee e = new Employee(
    "Ramesh",
    30,
    "Architect",
    "Bangalore",
    true,
    150000,
    "Java");

Use Builder

Employee employee = Employee.builder()
        .name("Ramesh")
        .age(30)
        .city("Bangalore")
        .designation("Architect")
        .salary(150000)
        .build();

Builder focuses only on object creation.

Employee focuses only on business data.

SRP improved.


SOLID + Spring Boot Mapping

Spring Boot FeatureSOLID PrinciplePattern
@ServiceSRPService Layer
@RepositorySRPRepository
Dependency InjectionDIPIoC
@Autowired ConstructorDIPDependency Injection
Strategy Bean SelectionOCPStrategy
JpaRepositoryISPRepository Interface
Bean PolymorphismLSPFactory
RestTemplateBuilderSRPBuilder
ResponseEntityOCPBuilder

Complete Mapping

Design PatternSOLID Principle
StrategyOCP
DecoratorOCP
Factory MethodDIP + LSP
Abstract FactoryDIP
BuilderSRP
AdapterISP
BridgeISP + DIP
ObserverOCP
CommandOCP + DIP
Template MethodOCP + LSP
ProxyOCP
FacadeSRP
RepositorySRP
Singleton(Not directly tied to SOLID; often overused and can violate SRP/DIP if misapplied)

Interview Cheat Sheet

Interview QuestionBest Answer
Which pattern best demonstrates OCP?Strategy and Decorator
Which pattern demonstrates DIP?Factory, Abstract Factory, Dependency Injection
Which pattern uses LSP?Factory Method, Template Method
Which pattern follows ISP?Adapter, Bridge
Which pattern improves SRP?Repository, Facade, Builder, Service Layer

Tip for Senior Java Architect Interviews

Rather than memorizing mappings, explain the reasoning:

  • Strategy supports OCP because you add new algorithms without modifying existing client code.

  • Decorator supports OCP by extending behavior dynamically instead of changing the original class.

  • Factory and Dependency Injection support DIP by ensuring clients depend on interfaces rather than concrete implementations.

  • Repository and Service Layer encourage SRP by separating persistence, business logic, and presentation concerns.

  • Adapter supports ISP by exposing only the operations a client actually needs.

  • Template Method supports LSP because subclasses provide specialized behavior while preserving the base class contract.

This explanation demonstrates a deeper understanding than simply listing patterns against SOLID principles, which is often what interviewers for Senior Architect roles look for.

No comments:

Post a Comment