Design Principle: Single Responsibility Principle(SRP)

Summary

Principle NameSingle Responsibility Principle
AcronymSRP
Other NamesClass Consistency Principle(CCP)
Principle Type SOLID
TaglineA class should have only a single responsibility
Implementing
Design Patterns
Adapter Pattern
Command Pattern
Decorator Pattern
Facade Pattern
Factory Pattern
Mediator Pattern
Observer Pattern
Strategy Pattern
Related Principles Open/Closed Principle(OCP)
Liskov Substitution Principle(LSP)
Interface Segregation Principle(ISP)
Dependency Inversion Principle(DIP)

Definition

Single Responsibility Principle(SRP) is the first principle of the SOLID Principles.

Single Responsibility is a basic concept in software design, that says when we define a class- we should not give it more than one responsibility.

This Single Responsibility Principle(SRP) says-

A class should have only one responsibility (and a single reason for it to be changed).

Class should have only one job/responsibility, and that job/responsibility should be encapsulated inside that class.

A class should deal with only one subject/entity. SRP encourages us to create classes that are focused(towards only one subject), and selfish(only concerned about its own elements).

For example, if a class is dealing with employees, then it should only be responsible for employee information, and actions. It should not directly deal with employee salary, attendance, etc. We should define separate classes for salary, tax, etc., and separate class(es) for attendance. Then the Employee class can interact with those classes.

Also, we only make changes to the Employee class for only something related to the employee directly. For salary or attendance, we would change the relevant classes.

Single responsibility leads to smaller classes, which are combined and composed to create a complex system. This makes the code –

More readable.
Easily maintainable.
Easy to test.

This principle ensures maximum cohesion and minimum coupling. Cohesion indicates, how single-minded the class is.

WARNING

If a class has multiple responsibilities, then it should be refactored to multiple smaller classes(each of which has a single responsibility).

Implementation

Use the following steps to ensure the Single Responsibility Principle-

Break a large class(with lots of responsibility) into small classes, based on the entity and responsibility.
Compose and combine these classes(and/or objects of the classes) to create the full system.

Example 1#: Employee Information

Let’s consider an example that handles employee information, with salary, taxes and attendance tracking-

General Implementation (without SRP)

class Employee {
    private int id;
    private String name;

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getInfo() {
        return "Employee id: " + id + ", name: " + name;
    }

    public void calculateSalary() {
        // Full logic to calculate the employee's salary
        System.out.println("Calculating salary");
    }

    public void calculateTax() {
        // Full logic to calculate the tax
        System.out.println("Calculating tax");
    }

    public void trackAttendance() {
        // Full logic to track employee attendance
        System.out.println("Tracking attendance");
    }

    public static void main(String[] args) {
        Employee employee = new Employee(1, "Bigbox Emp");
        System.out.println(employee.getInfo());
        employee.calculateSalary();
        employee.calculateTax();
        employee.trackAttendance();
    }
}
Java
class Employee:
    def __init__(self, id: int, name: str) -> None:
        self.id = id
        self.name = name

    def get_info(self) -> str:
        return f"Employee id: {self.id}, name: {self.name}"

    def calculate_salary(self) -> None:
        """Full logic to calculate the employee's salary"""

        print("Calculating salary")

    def calculate_tax(self) -> None:
        """Full logic to calculate the tax"""

        print("Calculating tax")

    def track_attendance(self) -> None:
        """Full logic to track employee attendance"""

        print("Tracking attendence")


# Demo usage
if __name__ == "__main__":
    employee = Employee(1, "Bigbox Emp")
    print(employee.get_info())
    employee.calculate_salary()
    employee.calculate_tax()
    employee.track_attendance()
Python
package main

import "fmt"

type Employee struct {
    ID   int
    Name string
}

func (e Employee) GetInfo() string {
    return fmt.Sprintf("Employee id: %d, name: %s", e.ID, e.Name)
}

func (e Employee) CalculateSalary() {
    // Full logic to calculate the employee's salary
    fmt.Println("Calculating salary")
}

func (e Employee) CalculateTax() {
    // Full logic to calculate the tax
    fmt.Println("Calculating tax")
}

func (e Employee) TrackAttendance() {
    // Full logic to track employee attendance
    fmt.Println("Tracking attendance")
}

func main() {
    employee := Employee{ID: 1, Name: "Bigbox Emp"}
    fmt.Println(employee.GetInfo())
    employee.CalculateSalary()
    employee.CalculateTax()
    employee.TrackAttendance()
}
Go
<?php

class Employee {
    private $id;
    private $name;

    public function __construct(int $id, string $name) {
        $this->id = $id;
        $this->name = $name;
    }

    public function getInfo(): string {
        return "Employee id: $this->id, name: $this->name";
    }

    public function calculateSalary(): void {
        // Full logic to calculate the employee's salary
        echo "Calculating salary\n";
    }

    public function calculateTax(): void {
        // Full logic to calculate the tax
        echo "Calculating tax\n";
    }

    public function trackAttendance(): void {
        // Full logic to track employee attendance
        echo "Tracking attendance\n";
    }
}

// Demo usage
$employee = new Employee(1, "Bigbox Emp");
echo $employee->getInfo() . "\n";
$employee->calculateSalary();
$employee->calculateTax();
$employee->trackAttendance();
PHP
class Employee {
    private id: number;
    private name: string;

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

    getInfo(): string {
        return `Employee id: ${this.id}, name: ${this.name}`;
    }

    calculateSalary(): void {
        console.log("Calculating salary");
    }

    calculateTax(): void {
        console.log("Calculating tax");
    }

    trackAttendance(): void {
        console.log("Tracking attendance");
    }
}

// Demo usage
const employee = new Employee(1, "Bigbox Emp");

console.log(employee.getInfo());
employee.calculateSalary();
employee.calculateTax();
employee.trackAttendance();
TypeScript

Implementation with SRP

class Employee {
    private int id;
    private String name;

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getInfo() {
        return "Employee id: " + id + ", name: " + name;
    }
}

class SalaryCalculator {
    public void calculateSalary(Employee employee) {
        System.out.println("Calculating salary for " + employee.getInfo());
    }
}

class TaxCalculator {
    public void calculateTax(Employee employee) {
        System.out.println("Calculating tax for " + employee.getInfo());
    }
}

class AttendanceTracker {
    public void trackAttendance(Employee employee) {
        System.out.println("Tracking attendance for " + employee.getInfo());
    }
}

public class Main {
    public static void main(String[] args) {
        Employee employee = new Employee(1, "Bigbox Emp");

        SalaryCalculator salaryCalculator = new SalaryCalculator();
        TaxCalculator taxCalculator = new TaxCalculator();
        AttendanceTracker attendanceTracker = new AttendanceTracker();

        System.out.println(employee.getInfo());
        salaryCalculator.calculateSalary(employee);
        taxCalculator.calculateTax(employee);
        attendanceTracker.trackAttendance(employee);
    }
}
Java
class Employee:
    def __init__(self, id: int, name: str) -> None:
        self.id = id
        self.name = name

    def get_info(self) -> str:
        return f"Employee id: {self.id}, name: {self.name}"


class SalaryCalculator:
    def calculate_salary(self, employee: Employee) -> None:
        """Full logic to calculate the employee's salary"""

        print(f"Calculating salary for {employee.name}")


class TaxCalculator:
    def calculate_tax(self, employee: Employee) -> None:
        """Full logic to calculate the employee's tax"""

        print(f"Calculating tax for {employee.name}")


class AttendanceTracker:
    def track_attendance(self, employee: Employee) -> None:
        """Full logic to track employee attendance"""

        print(f"Tracking attendance for {employee.name}")


# Demo usage
if __name__ == "__main__":
    employee = Employee(1, "Bigbox Emp")

    # Using separate classes for different responsibilities
    salary_calculator = SalaryCalculator()
    tax_calculator = TaxCalculator()
    attendance_tracker = AttendanceTracker()

    print(employee.get_info())

    salary_calculator.calculate_salary(employee)
    tax_calculator.calculate_tax(employee)
    attendance_tracker.track_attendance(employee)
Python
package main

import "fmt"

type Employee struct {
    ID   int
    Name string
}

func (e Employee) GetInfo() string {
    return fmt.Sprintf("Employee id: %d, name: %s", e.ID, e.Name)
}

type SalaryCalculator struct{}

func (s SalaryCalculator) CalculateSalary(employee Employee) {
    fmt.Printf("Calculating salary for %s\n", employee.GetInfo())
}

type TaxCalculator struct{}

func (t TaxCalculator) CalculateTax(employee Employee) {
    fmt.Printf("Calculating tax for %s\n", employee.GetInfo())
}

type AttendanceTracker struct{}

func (a AttendanceTracker) TrackAttendance(employee Employee) {
    fmt.Printf("Tracking attendance for %s\n", employee.GetInfo())
}

func main() {
    employee := Employee{ID: 1, Name: "Bigbox Emp"}

    salaryCalculator := SalaryCalculator{}
    taxCalculator := TaxCalculator{}
    attendanceTracker := AttendanceTracker{}

    fmt.Println(employee.GetInfo())
    salaryCalculator.CalculateSalary(employee)
    taxCalculator.CalculateTax(employee)
    attendanceTracker.TrackAttendance(employee)
}
Go
<?php

class Employee {
    private $id;
    private $name;

    public function __construct(int $id, string $name) {
        $this->id = $id;
        $this->name = $name;
    }

    public function getInfo(): string {
        return "Employee id: $this->id, name: $this->name";
    }
}

class SalaryCalculator {
    public function calculateSalary(Employee $employee): void {
        echo "Calculating salary for " . $employee->getInfo() . "\n";
    }
}

class TaxCalculator {
    public function calculateTax(Employee $employee): void {
        echo "Calculating tax for " . $employee->getInfo() . "\n";
    }
}

class AttendanceTracker {
    public function trackAttendance(Employee $employee): void {
        echo "Tracking attendance for " . $employee->getInfo() . "\n";
    }
}

// Demo usage
$employee = new Employee(1, "Bigbox Emp");

$salaryCalculator = new SalaryCalculator();
$taxCalculator = new TaxCalculator();
$attendanceTracker = new AttendanceTracker();

echo $employee->getInfo() . "\n";
$salaryCalculator->calculateSalary($employee);
$taxCalculator->calculateTax($employee);
$attendanceTracker->trackAttendance($employee);
PHP
class Employee {
    private id: number;
    private name: string;

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

    getInfo(): string {
        return `Employee id: ${this.id}, name: ${this.name}`;
    }
}

class SalaryCalculator {
    calculateSalary(employee: Employee): void {
        console.log(`Calculating salary for ${employee.getInfo()}`);
    }
}

class TaxCalculator {
    calculateTax(employee: Employee): void {
        console.log(`Calculating tax for ${employee.getInfo()}`);
    }
}

class AttendanceTracker {
    trackAttendance(employee: Employee): void {
        console.log(`Tracking attendance for ${employee.getInfo()}`);
    }
}

// Demo usage
const employee = new Employee(1, "Bigbox Emp");

const salaryCalculator = new SalaryCalculator();
const taxCalculator = new TaxCalculator();
const attendanceTracker = new AttendanceTracker();

console.log(employee.getInfo());
salaryCalculator.calculateSalary(employee);
taxCalculator.calculateTax(employee);
attendanceTracker.trackAttendance(employee);
TypeScript

Example #2: Report Generation

In this example, we are considering report generation(in pdf format) and sending the report-

General Implementation (without SRP)

class Report {

    public void generateReport() {
        processReportData();
        generatePDF();
    }

    public void processReportData() {
        // Full logic for getting and processing data for the report
        System.out.println("Getting data for report, and processing data.");
    }

    public void generatePDF() {
        // Full logic for generating PDF from the processed data
        System.out.println("Generating PDF from data");
    }

    public void sendReport() {
        // Code for sending the report (via email)
        System.out.println("Sending report via email");
    }

    public static void main(String[] args) {
        Report report = new Report();
        report.generateReport();
        report.sendReport();
    }
}
Java
class Report:
    def generate_report(self):
        self.process_report_data()
        self.generate_pdf()
    
    def process_report_data(self):
        '''Full logic of getting and processing data for the report'''
        
        print("Getting data for report, and processing data.")
    
    def generate_pdf(self):
        '''Full processing code for generating PDF from the processed data'''
        print("Generating pdf from data")
    
    def send_report(self):
        '''Code for sending report(via email)'''
        
        print("Sending report via email")
        
        
# Demo useage
if __name__ == "__main__":
    report = Report()
    report.generate_report()
    report.send_report()
Python
package main

import "fmt"

type Report struct{}

func (r Report) GenerateReport() {
    r.ProcessReportData()
    r.GeneratePDF()
}

func (r Report) ProcessReportData() {
    fmt.Println("Getting data for report, and processing data.")
}

func (r Report) GeneratePDF() {
    fmt.Println("Generating PDF from data")
}

func (r Report) SendReport() {
    fmt.Println("Sending report via email")
}

func main() {
    report := Report{}
    report.GenerateReport()
    report.SendReport()
}
Go
<?php

class Report {

    public function generateReport() {
        $this->processReportData();
        $this->generatePDF();
    }

    public function processReportData() {
        // Full logic for getting and processing data for the report
        echo "Getting data for report, and processing data.\n";
    }

    public function generatePDF() {
        // Full logic for generating PDF from the processed data
        echo "Generating PDF from data\n";
    }

    public function sendReport() {
        // Code for sending the report (via email)
        echo "Sending report via email\n";
    }
}

// Demo usage
$report = new Report();
$report->generateReport();
$report->sendReport();
PHP
class Report {

    generateReport(): void {
        this.processReportData();
        this.generatePDF();
    }

    processReportData(): void {
        // Full logic for getting and processing data for the report
        console.log("Getting data for report, and processing data.");
    }

    generatePDF(): void {
        // Full logic for generating PDF from the processed data
        console.log("Generating PDF from data");
    }

    sendReport(): void {
        // Code for sending the report (via email)
        console.log("Sending report via email");
    }
}

// Demo usage
const report = new Report();
report.generateReport();
report.sendReport();
TypeScript

Implementation with SRP

class ReportDataProcessor {
    public String processReportData() {
        // Full logic for getting and processing data for the report
        System.out.println("Getting data for report, and processing data.");
        return "Processed Report Data";  // Return the processed data
    }
}

class PDFGenerator {
    public String generatePDF(String data) {
        // Full processing code for generating PDF from the processed data
        System.out.println("Generating PDF from data: " + data);
        return "Report.pdf";  // Return the generated PDF
    }
}

class ReportSender {
    public void sendReport(String pdf) {
        // Code for sending the report (via email)
        System.out.println("Sending report: " + pdf + " via email");
    }
}

public class Main {
    public static void main(String[] args) {
        ReportDataProcessor dataProcessor = new ReportDataProcessor();
        PDFGenerator pdfGenerator = new PDFGenerator();
        ReportSender reportSender = new ReportSender();

        // Process report data
        String processedData = dataProcessor.processReportData();

        // Generate PDF from the processed data
        String pdfFile = pdfGenerator.generatePDF(processedData);

        // Send the generated PDF report
        reportSender.sendReport(pdfFile);
    }
}
Java
class ReportDataProcessor:
    def process_report_data(self):
        """Full logic of getting and processing data for the report"""

        print("Getting data for report, and processing data.")
        return "Processed Report Data"  # Return the processed data


class PDFGenerator:
    def generate_pdf(self, data):
        """Full processing code for generating PDF from the processed data"""

        print(f"Generating PDF from data: {data}")
        return "Report.pdf"  # Return the generated PDF


class ReportSender:
    def send_report(self, pdf):
        """Code for sending report (via email)"""

        print(f"Sending report: {pdf} via email")


# Demo usage
if __name__ == "__main__":
    data_processor = ReportDataProcessor()
    pdf_generator = PDFGenerator()
    report_sender = ReportSender()
    
    # Process report data
    processed_data = data_processor.process_report_data()
    
    # Generate PDF from the processed data
    pdf_file = pdf_generator.generate_pdf(processed_data)
    
    # Send the generated PDF report
    report_sender.send_report(pdf_file)
Python
package main

import "fmt"

type ReportDataProcessor struct{}

func (r ReportDataProcessor) ProcessReportData() string {
    fmt.Println("Getting data for report, and processing data.")
    return "Processed Report Data"
}

type PDFGenerator struct{}

func (p PDFGenerator) GeneratePDF(data string) string {
    fmt.Printf("Generating PDF from data: %s\n", data)
    return "Report.pdf"
}

type ReportSender struct{}

func (r ReportSender) SendReport(pdf string) {
    fmt.Printf("Sending report: %s via email\n", pdf)
}

func main() {
    dataProcessor := ReportDataProcessor{}
    pdfGenerator := PDFGenerator{}
    reportSender := ReportSender{}

    // Process report data
    processedData := dataProcessor.ProcessReportData()

    // Generate PDF from the processed data
    pdfFile := pdfGenerator.GeneratePDF(processedData)

    // Send the generated PDF report
    reportSender.SendReport(pdfFile)
}
Go
<?php

class ReportDataProcessor {
    public function processReportData() {
        // Full logic for getting and processing data for the report
        echo "Getting data for report, and processing data.\n";
        return "Processed Report Data";  // Return the processed data
    }
}

class PDFGenerator {
    public function generatePDF(string $data) {
        // Full processing code for generating PDF from the processed data
        echo "Generating PDF from data: $data\n";
        return "Report.pdf";  // Return the generated PDF
    }
}

class ReportSender {
    public function sendReport(string $pdf) {
        // Code for sending the report (via email)
        echo "Sending report: $pdf via email\n";
    }
}

// Demo usage
$dataProcessor = new ReportDataProcessor();
$pdfGenerator = new PDFGenerator();
$reportSender = new ReportSender();

// Process report data
$processedData = $dataProcessor->processReportData();

// Generate PDF from the processed data
$pdfFile = $pdfGenerator->generatePDF($processedData);

// Send the generated PDF report
$reportSender->sendReport($pdfFile);
PHP
class ReportDataProcessor {
    processReportData(): string {
        // Full logic for getting and processing data for the report
        console.log("Getting data for report, and processing data.");
        return "Processed Report Data";  // Return the processed data
    }
}

class PDFGenerator {
    generatePDF(data: string): string {
        // Full processing code for generating PDF from the processed data
        console.log(`Generating PDF from data: ${data}`);
        return "Report.pdf";  // Return the generated PDF
    }
}

class ReportSender {
    sendReport(pdf: string): void {
        // Code for sending the report (via email)
        console.log(`Sending report: ${pdf} via email`);
    }
}

// Demo usage
const dataProcessor = new ReportDataProcessor();
const pdfGenerator = new PDFGenerator();
const reportSender = new ReportSender();

// Process report data
const processedData = dataProcessor.processReportData();

// Generate PDF from the processed data
const pdfFile = pdfGenerator.generatePDF(processedData);

// Send the generated PDF report
reportSender.sendReport(pdfFile);
TypeScript

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.