Course program← Объектно ориентированное программирование
1История ООП
2Объект и Класс
3Базовые концепции ООП
4Принцип проектирования GRASP
5Принцип проектирования SOLID
6Паттерны GOF
7Методические материалы

Polymorphism

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.