DEV Community

Cover image for Single Responsibility Principle

Posted on • Updated on

Single Responsibility Principle



Info : Single-responsibility principle - Wikipedia

The Single Responsibility Principle (SRP) is one of the SOLID principles of object-oriented programming. It states that a class should have only one reason to change. In other words, a class should have only one responsibility or job.

node dist/srp.js
Enter fullscreen mode Exit fullscreen mode

Each class should focus on a single responsibility or task.

Example: E-commerce Order System

  • Imagine an e-commerce platform with an order processing system.
  • The system handles various tasks:
    • Product management
    • Order creation
    • Pricing calculation
    • Invoice generation
    • Payment processing
Enter fullscreen mode Exit fullscreen mode

Breaking Down Responsibilities

  • Product Class (Product.ts):
    • Responsible for representing product details (ID, name, price).
  • Order Class (Order.ts):
    • Manages the list of products in an order.
    • Adds and retrieves products.
  • Invoice Class (invoice.ts):
    • Generates an invoice for an order.
    • Displays product names and prices.
  • PaymentProcessor Class (PaymentProcessor.ts):
    • Handles payment processing.
    • Sends emails and updates accounting.
  • PricingCalculator Class (PricingCalculator.ts):
    • Calculates the total price of an order.


export class Product {

    constructor(id: string, name: string, price: number) { = id; = name;
        this.price = price;

    id : string;
    name : string;
    price : number;
Enter fullscreen mode Exit fullscreen mode


import {Product} from "./Product";
export class Order {
    product: Product [] = []

    addProduct(product: Product) {

    getProduct() {
        return this.product
Enter fullscreen mode Exit fullscreen mode


import {Product} from "../Product";

export class Invoice {

    generateInvoice(product: Product[] , amount: number) {
        Invoice Date : ${new Date().toLocaleString()}

        Product Name\t\t\tPrice

        product.forEach((product:Product)=> {

        console.log(`Total Price : ${amount}`)
Enter fullscreen mode Exit fullscreen mode


import {Order} from "../Order";

export class PaymentProcessor {
    processPayment(order: Order) {
        console.log(`Processing payment...`)
        console.log(`Payment processed successfully.`)
        console.log(`Added to accounting system!`)
        console.log(`Email sent to customer!`)
Enter fullscreen mode Exit fullscreen mode


import {Product} from "../Product";

export class PricingCalculator {
    calculatePricing(products: Product[]): number {
        return products.reduce((acc, product) => acc + product.price, 0);
Enter fullscreen mode Exit fullscreen mode

Exceptions and Violations

  • Exceptions:
    • Sometimes, combining responsibilities is necessary for efficiency.
    • For example, tightly coupling pricing calculation and order management might be acceptable.
  • Violations:

    // Product class representing product details
    class Product {
        constructor(public id: string, public name: string, public price: number) {}
    // Imagine an Order class that handles both order management and payment processing
    class Order {
        private orderID: string;
        private products: Product[];
        constructor(orderID: string) {
            this.orderID = orderID;
            this.products = [];
        addProduct(product: Product) {
        calculateTotalPrice(): number {
            return this.products.reduce((total, product) => total + product.price, 0);
        processPayment(paymentMethod: string) {
            // Process payment logic here
            console.log(`Payment for order ${this.orderID} processed via ${paymentMethod}`);
    // Usage
    const product1 = new Product("1", "Laptop", 200000);
    const product2 = new Product("2", "Phone", 60000);
    const order = new Order("123");
    const total = order.calculateTotalPrice();
    console.log(`Total price: ${total}`);
    order.processPayment("Credit Card");
    • The Order class combines two distinct responsibilities: managing the list of products (order management) and processing payments.
    • Violation: If payment processing logic changes, it impacts the Order class, which should focus only on order management.
    • Solution: Separate payment processing into a dedicated class (e.g., PaymentProcessor). Each class should have a clear purpose to adhere to SRP.


import {Product, Order, PricingCalculator, Invoice ,PaymentProcessor} from "./order";

const product1 = new Product("1","Laptop", 200000);
const product2 = new Product("2","Phone", 60000);
const product3 = new Product("3", "Car", 8000000);

const order = new Order();


const pricingCalculator = new PricingCalculator();
const total = pricingCalculator.calculatePricing(order.getProduct());

const invoice = new Invoice();
invoice.generateInvoice(order.getProduct(), total);

const paymentProcessor = new PaymentProcessor();
Enter fullscreen mode Exit fullscreen mode


  • SRP ensures that each class has a clear purpose.
  • By separating concerns, we improve maintainability and reduce the impact of changes.
  • In our e-commerce example, adhering to SRP leads to a more robust and flexible system.

Top comments (0)