The Open-Closed Principle (OCP) says that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
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.
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!