Links
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.
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
|—order
|—Order.ts
|—Product.ts
|—Jobs
|—invoice.ts
|—PaymentProcessor.ts
|—PricingCalculator.ts
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
constructor(id: string, name: string, price: number) {
this.id = id;
this.name = name;
this.price = price;
}
id : string;
name : string;
price : number;
}
order/Order.ts
export class Order {
product: Product [] = []
addProduct(product: Product) {
this.product.push(product)
}
getProduct() {
return this.product
}
}
order/Jobs/invoice.ts
export class Invoice {
generateInvoice(product: Product[] , amount: number) {
console.log(`
Invoice Date : ${new Date().toLocaleString()}
_____________________________
Product NametttPrice
`);
product.forEach((product:Product)=> {
console.log(`${product.name}ttt${product.price}`)
});
console.log(`_____________________________`);
console.log(`Total Price : ${amount}`)
}
}
order/Jobs/PaymentProcessor.ts
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!`)
}
}
order/Jobs/PricingCalculator.ts
export class PricingCalculator {
calculatePricing(products: Product[]): number {
return products.reduce((acc, product) => acc + product.price, 0);
}
}
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
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);
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.