Chaturmind
LearnDSASystem DesignBlogPremium
Sign inGet started
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML

Company

  • Blog
  • Premium
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight
Chaturmind
← Low-Level Design & Design Patterns

OOP Fundamentals & SOLID Principles

  • Object-Oriented Design Refresher
  • Single Responsibility & Open/Closed
  • Liskov Substitution & Interface Segregation
  • Dependency Inversion

Creational Design Patterns

  • Singleton Pattern
  • Factory & Abstract Factory
  • Builder Pattern
  • Prototype Pattern

Structural Design Patterns

  • Adapter & Facade
  • Decorator Pattern
  • Composite & Proxy
  • Bridge & Flyweight
HomeLearnSystem DesignLow-Level Design & Design PatternsOOP Fundamentals & SOLID Principles
✓ FreeIntermediate· 8 min read

Single Responsibility & Open/Closed

SRP and OCP explained through a refactor, not a definition: turning a 'PaymentProcessor' god-class into something that can grow without being edited.

Published September 22, 2026


Single Responsibility & Open/Closed

The first two of the five SOLID principles, and the two an interviewer is most likely to make you apply live rather than just define.

SRP: one class, one reason to change

The common misreading is "a class should do one thing" — closer, but the precise version (Robert Martin's own phrasing) is a class should have only one reason to change. "One thing" is vague; "one reason to change" is testable: ask who would ask you to modify this class, and why. If the answer branches ("the finance team wants X, but also the logging format changed, but also validation rules changed"), it violates SRP even if every method individually looks reasonable.

Spotting the violation

class PaymentProcessor {
    void validate(Order order) { /* business rule checks */ }
    void charge(Order order) { /* calls payment gateway */ }
    void logTransaction(Order order) { /* writes to a log file */ }
    void sendConfirmationEmail(Order order) { /* SMTP call */ }
}

Four reasons this class changes: a validation rule changes, the payment gateway's API changes, the logging format changes, or the email template changes. Four unrelated teams can each have a reason to modify this one file — that's the smell, not any single method being "too long."

The refactor

class OrderValidator { void validate(Order order) { ... } }
class PaymentGateway { void charge(Order order) { ... } }
class TransactionLogger { void log(Order order) { ... } }
class EmailNotifier { void sendConfirmation(Order order) { ... } }

class PaymentProcessor {
    private final OrderValidator validator;
    private final PaymentGateway gateway;
    private final TransactionLogger logger;
    private final EmailNotifier notifier;
    // constructor takes all four (dependency injection)

    void process(Order order) {
        validator.validate(order);
        gateway.charge(order);
        logger.log(order);
        notifier.sendConfirmation(order);
    }
}

PaymentProcessor is now an orchestrator with one reason to change (the order of steps, or which steps run) — each concern lives in its own class with its own single reason to change. This also, as a side effect, makes each piece independently unit-testable — you can test OrderValidator without mocking a payment gateway.

OCP: open for extension, closed for modification

A class should be extendable to support new behavior without editing its existing, already-tested code. The canonical fix is the Strategy pattern.

The violation

class PaymentGateway {
    void charge(Order order, String method) {
        if (method.equals("CREDIT_CARD")) { /* ... */ }
        else if (method.equals("PAYPAL")) { /* ... */ }
        else if (method.equals("CRYPTO")) { /* ... */ } // every new method = edit this class
    }
}

Every new payment method means opening this class, adding a branch, and re-testing all existing branches for regression risk — the opposite of "closed for modification."

The OCP fix (Strategy)

interface PaymentStrategy { void charge(Order order); }
class CreditCardStrategy implements PaymentStrategy { public void charge(Order order) { ... } }
class PayPalStrategy implements PaymentStrategy { public void charge(Order order) { ... } }

class PaymentGateway {
    void charge(Order order, PaymentStrategy strategy) {
        strategy.charge(order); // never needs to change for a new payment method
    }
}

Adding crypto support now means writing a new CryptoStrategy class — zero changes to PaymentGateway or any existing strategy, zero regression risk to code that was already tested and shipped.

How SRP and OCP work together here

They're not independent — the SRP refactor (splitting concerns into their own classes) is often the precondition that makes an OCP-compliant extension point possible in the first place. You can't cleanly plug in a new PaymentStrategy if payment logic is still tangled inside a god-class doing four other things.

Follow-up questions this topic invites — and their answers

Q: Doesn't splitting one class into five just move complexity around instead of removing it? A: It relocates complexity from within one class (high internal coupling, many reasons to change) to between small classes with a single clear responsibility each — the total complexity of the system doesn't vanish, but each individual piece becomes easier to reason about, test, and change independently, which is the actual payoff.

Q: Can you take SRP too far? A: Yes — over-splitting into many trivial one-method classes adds indirection without a real independent "reason to change" for each, making the codebase harder to navigate for no benefit. SRP is a judgment call anchored to actual independent change drivers, not a mandate to minimize class size.

Q: How does OCP relate to the Open/Closed violation of a giant switch/if-else chain in general, not just payments? A: Any branch-per-type-of-thing structure (switch on an enum, if/else chain on a string type) is an OCP smell whenever new types get added over time — Strategy (or polymorphism generally, via an interface implemented per type) is the general-purpose fix, not something specific to payments.

Previous

Object-Oriented Design Refresher

Next

Liskov Substitution & Interface Segregation

AI Tutor

Lesson: Single Responsibility & Open/Closed

Quick actions

AI responses can be inaccurate. Verify critical information.