This lesson was translated automatically – it may contain errors.
This is the very same polymorphism we took apart in the basic concepts – just seen from another side. There we looked at how it works: virtual methods, interfaces, the implementation chosen by the actual type. Here – when to reach for it: GRASP says that branching on a type is a signal, and the responsibility should be handed to the types themselves.
The problem the rule solves reads like this: handling alternative variants of behaviour
depending on the type. It should be solved with polymorphic operations rather than a type
check and conditional logic – then the system grows by new variants instead of new branches
in an existing switch.
// A type check: every new format means editing this method
public string Export(Report report, string format)
{
if (format == "pdf") return ExportPdf(report);
if (format == "csv") return ExportCsv(report);
throw new NotSupportedException(format);
}
// Polymorphism: a new format is a new class, the old code stays untouched
public interface IReportExporter
{
string Export(Report report);
}
public class PdfExporter : IReportExporter { public string Export(Report r) => ""; }
public class CsvExporter : IReportExporter { public string Export(Report r) => ""; }
// A value check: every new format means editing this function
std::string Export(const Report& report, const std::string& format) {
if (format == "pdf") return ExportPdf(report);
if (format == "csv") return ExportCsv(report);
throw std::invalid_argument("unsupported format: " + format);
}
// Polymorphism: a new format is a new class, the old code stays untouched
class IReportExporter {
public:
virtual std::string Export(const Report& report) const = 0;
virtual ~IReportExporter() = default;
};
class PdfExporter : public IReportExporter {
public:
std::string Export(const Report& report) const override { return ""; }
};
class CsvExporter : public IReportExporter {
public:
std::string Export(const Report& report) const override { return ""; }
};
// A value check: every new format means editing this function
func Export(report Report, format string) (string, error) {
switch format {
case "pdf":
return exportPDF(report), nil
case "csv":
return exportCSV(report), nil
}
return "", fmt.Errorf("unsupported format: %s", format)
}
// Polymorphism through an interface: a new format is a new type
type ReportExporter interface {
Export(report Report) string
}
type PDFExporter struct{}
func (PDFExporter) Export(r Report) string { return "" }
type CSVExporter struct{}
func (CSVExporter) Export(r Report) string { return "" }
# A type check: every new format means editing this function
def export(report: Report, fmt: str) -> str:
if fmt == "pdf":
return export_pdf(report)
if fmt == "csv":
return export_csv(report)
raise ValueError(f"unsupported format: {fmt}")
# Polymorphism: a new format is a new class
from typing import Protocol
class ReportExporter(Protocol):
def export(self, report: Report) -> str: ...
class PdfExporter:
def export(self, report: Report) -> str:
return ""
class CsvExporter:
def export(self, report: Report) -> str:
return ""
// A type check: every new format means editing this function
function exportReport(report: Report, format: string): string {
if (format === "pdf") return exportPdf(report);
if (format === "csv") return exportCsv(report);
throw new Error(`unsupported format: ${format}`);
}
// Polymorphism: a new format is a new class
interface ReportExporter {
export(report: Report): string;
}
class PdfExporter implements ReportExporter {
export(report: Report): string {
return "";
}
}
class CsvExporter implements ReportExporter {
export(report: Report): string {
return "";
}
}
// A type check: every new format means editing this method
public String export(Report report, String format) {
if (format.equals("pdf")) return exportPdf(report);
if (format.equals("csv")) return exportCsv(report);
throw new UnsupportedOperationException(format);
}
// Polymorphism: a new format is a new class
public interface ReportExporter {
String export(Report report);
}
public class PdfExporter implements ReportExporter {
public String export(Report report) { return ""; }
}
public class CsvExporter implements ReportExporter {
public String export(Report report) { return ""; }
}
// A type check: every new format means editing this function
fun export(report: Report, format: String): String = when (format) {
"pdf" -> exportPdf(report)
"csv" -> exportCsv(report)
else -> throw UnsupportedOperationException(format)
}
// Polymorphism: a new format is a new implementation
fun interface ReportExporter {
fun export(report: Report): String
}
class PdfExporter : ReportExporter {
override fun export(report: Report): String = ""
}
class CsvExporter : ReportExporter {
override fun export(report: Report): String = ""
}
The same principle underlies the Strategy and State patterns from the last module of the course.