Single Responsibility Principle

codexam

Subham

Posted on April 27, 2024

Single Responsibility Principle

Links

REPO
Linkedin
Github

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
|---srp.ts 
|---order
    |---Order.ts
    |---Product.ts   
    |---Jobs
         |---invoice.ts
         |---PaymentProcessor.ts
         |---PricingCalculator.ts
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.

order/Product.ts

export class Product {

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

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

order/Order.ts

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

    addProduct(product: Product) {
        this.product.push(product)
    }

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

order/Jobs/invoice.ts

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

export class Invoice {

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

        Product Name\t\t\tPrice
        `);

        product.forEach((product:Product)=> {
            console.log(`${product.name}\t\t\t${product.price}`)
        });

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

order/Jobs/PaymentProcessor.ts

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

order/Jobs/PricingCalculator.ts

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) {
            this.products.push(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");
    order.addProduct(product1);
    order.addProduct(product2);
    
    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.

srp.ts

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();

order.addProduct(product1);
order.addProduct(product2);
order.addProduct(product3);

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

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

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

Summary

  • 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.
💖 💪 🙅 🚩
codexam
Subham

Posted on April 27, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Single Responsibility Principle
systemdesign Single Responsibility Principle

April 27, 2024