Open-Closed Principle

The Open-Closed Principle (OCP) says that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

OCP violation example

Let’s look at a ReportGenerator class that generates reports in PDF and CSV formats.

class PDFReport {
  generatePDF() {...}
}

class CSVReport {
  generateCSV() {...}
}

class ReportGenerator {
  generateReport(report: PDFReport | CSVReport) {
    if (report instanceof PDFReport) {
      report.generatePDF();
    } else if (report instanceof CSVReport) {
      report.generateCSV();
    }
  }
}

This approach isn’t ideal because if we want to add a new report type, we have to modify the ReportGenerator class (adding more if statements 🤮). This violates OCP and makes the code messy.

Interfaces to the rescue

Instead, we can introduce an interface that every report class implements. By using interfaces, we don’t need to touch ReportGenerator when adding new report types. We just pass the Report which we want to generate.

interface Report {
  generate(): void;
}

class PDFReport implements Report {
  generate() {...}
}

class CSVReport implements Report {
  generate() {...}
}

class HTMLReport implements Report {
  generate() {...}
}

class ReportGenerator {
  generateReport(report: Report) {
    report.generate();
  }
}

Now, if we want to add a new report format, we just create a new class that implements Report without modifying ReportGenerator.

It’s open for extension, but closed for modification.


Find this post helpful? Subscribe and get notified when I post something new!