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!