Продолжаем наше путешествие по миру объектно-ориентированного программирования. Мы уже рассмотрели наследование, инкапсуляцию и абстракцию. Теперь настало время для полиморфизма.
Когда я только начинала разбираться в ООП, полиморфизм был для меня самым непонятным: «Объекты, которые могут принадлежать к разным классам, но имеют одинаковое поведение, определенное через общий интерфейс или базовый класс». О как! Давайте попробуем разобраться!
Полиморфизм буквально означает «много форм». Представьте: у вас есть базовый класс, который описывает общее поведение, а в классах-наследниках вы можете переопределить это поведение в соответствии с конкретной реализацией. Это и есть полиморфизм.
Полиморфизм в Dart
В Dart можно выделить два основных типа полиморфизма:
1. Полиморфизм на основе наследования. Дочерние классы наследуют поведение родителя и могут его переопределять
2. Полиморфизм через интерфейсы. Класс реализует интерфейс и обязан описать все его методы. Это позволяет задать единое поведение для разных реализаций
Помните пример со статьями из поста про наследование? Тогда я создала базовый класс с общими методами. Представим, что материалов стало больше. Теперь хочется разделить их по категориям.
class Article {
const Article(this.title, this.author);
final String title;
final String author;
void write() => print('Пишем статью: "$title"');
void publish() => print('Публикация статьи "$title", автор: $author');
void section() => print('Общий раздел');
}
Теперь создадим подклассы с разными разделами:
class OOPArticle extends Article {
const OOPArticle(String title, String author) : super(title, author);
@override
void section() => print('🔹 Раздел: Объектно-ориентированное программирование');
}
class PatternsArticle extends Article {
const PatternsArticle(String title, String author) : super(title, author);
@override
void section() => print('🔸 Раздел: Паттерны проектирования');
}
Теперь мы можем использовать их полиморфно:
void printArticleInfo(Article article) {
article.write();
article.section();
article.publish();
}
void main() {
final articles = [
OOPArticle('Что такое полиморфизм?', 'Роза'),
PatternsArticle('Паттерн Singleton', 'Роза'),
];
for (final article in articles) {
printArticleInfo(article);
print('---');
}
}
Даже не зная точного типа статьи, мы можем обращаться к ней через базовый класс Article. В зависимости от конкретной реализации логика будет разной.
Кроме классического полиморфизма существуют и другие его формы:
▫️Полиморфизм подтипов (Subtype polymorphism)
Это наиболее привычный вариант, когда один класс наследуется от другого и переопределяет его поведение. Именно его чаще всего имеют в виду, говоря о полиморфизме в ООП.
▫️Параметрический полиморфизм (Generics)
Позволяет писать универсальный код, работающий с разными типами данных. Например:
T identity<T>(T value) => value;
void main() {
print(identity<int>(10)); // 10
print(identity<String>('Да')); // Да
}
Здесь функция identity не зависит от конкретного типа — она просто возвращает то, что получила. Тип указывается как параметр <T>.
▫️Ad hoc полиморфизм
Это перегрузка методов, когда у функции одно имя, но разные параметры. Но в Dart такая перегрузка не поддерживается напрямую.
А зачем это все?
Полиморфизм помогает писать чистый, расширяемый и переиспользуемый код, особенно когда работаешь с большим количеством похожих сущностей.
Но есть важное правило:
Не объединяйте все подряд в общее поведение. Интерфейс или базовый класс должен описывать действительно общее поведение. Иначе вы рискуете нарушить принцип единственной ответственности (SRP).
На этом все! Если понравилась статья, то ставьте 💙
