Introduction
The Single Responsibility Principle (SRP) states that a class should have only one reason to change. In other words, a class should have only one responsibility.
Let’s look at a class which is not following this principle:
import java.util.List;
class Invoice {
private List<Product> products;
public Invoice(List<Product> products) {
this.products = products;
}
public double calculateTotal() {
return products.stream().mapToDouble(Product::getPrice).sum();
}
public void printInvoice() {
products.forEach(product -> System.out.println(product.getDescription() + ": " + product.getPrice()));
System.out.println("Total: " + calculateTotal());
}
public void saveToDB(){
// saves to DB..
}
}
class Product {
private double price;
private String description;
public Product(double price, String description) {
this.price = price;
this.description = description;
}
public double getPrice() {
return price;
}
public String getDescription() {
return description;
}
}
It seems to have several responsibilities:
- Calculating the total amount - If the pricing logic changes, the
Invoice
class would need to be modified. - Saving data to a database - If you decide to change the database technology or the way data is saved, the
Invoice
class would need to be modified. - Printing the invoice - If the printing mechanism changes (e.g., from a physical print to digital format), you would need to modify the
Invoice
class.
Let’s segregate these responsibilities in separate classes.
Invoice.java
import java.util.List;
class Invoice {
private List<Product> products;
public Invoice(List<Product> products) {
this.products = products;
}
public double calculateTotal() {
return products.stream().mapToDouble(Product::getPrice).sum();
}
public List<Product> getProducts() {
return products;
}
}
Product.java
class Product {
private double price;
private String description;
public Product(double price, String description) {
this.price = price;
this.description = description;
}
public double getPrice() {
return price;
}
public String getDescription() {
return description;
}
}
InvoiceCalculator.java
public class InvoiceCalculator {
private Invoice invoice;
public InvoiceCalculator(Invoice invoice) {
this.invoice = invoice;
}
public int calculateTotal() {
return products.stream().mapToDouble(Product::getPrice).sum();
}
}
InvoicePersistence.java
class InvoicePersistence {
public void saveToDatabase(Invoice invoice) {
// Logic to save invoice details to a database
}
}
InvoicePrinter.java
public class InvoicePrinter {
private Invoice invoice;
public InvoicePrinter(Invoice invoice) {
this.invoice = invoice;
}
public void printInvoice() {
// Print the invoice
}
}
Each class has a single responsibility and can be developed, tested, and modified independently.