Начало работы с объектно-ориентированным Dart | Часть 2

Начало работы с объектно-ориентированным Dart | Часть 2
Начало работы с объектно-ориентированным Dart | Часть 2

Привет, если вы на пути изучения Flutter/Dart или вам просто интересно почитать про разработку подписывайтесь на мой канал в telegram, буду рад вас видеть! А сегодня поговорим про ООП в DART-е!

Добавление наследования класса

Проблема

Вы хотите улучшить существующий класс, введя дополнительные функциональные возможности, которых нет в исходном классе.

Решение

Используйте класс с ключевым словом extends, чтобы включить наследование от родительского класса. При использовании ключевого слова extends подкласс наследует функциональность суперкласса. Как объектно-ориентированный язык, Dart предоставляет обширную поддержку классов в каждой новой версии.

Вот пример того, как использовать расширения для добавления наследования классов в Dart:

class Media { String title = ""; String type = ""; Media(){ type = "Class"; } void setMediaTitle(String mediaTitle){ title = mediaTitle; } String getMediaTitle(){ return title; } String getMediaType(){ return type; } } class Book extends Media { String author = ""; String isbn = ""; Book(){ type = "Subclass"; } void setBookAuthor(String bookAuthor){ author = bookAuthor; } void setBookISBN(String bookISBN){ isbn = bookISBN; } String getBookTitle(){ return title; } String getBookAuthor(){ return author; } String getBookISBN(){ return isbn; } } void main() { var myMedia = Media(); myMedia.setMediaTitle('Tron'); print ('Title: ${myMedia.getMediaTitle()}'); print ('Type: ${myMedia.getMediaType()}'); var myBook = Book(); myBook.setMediaTitle("Jungle Book"); myBook.setBookAuthor("R Kipling"); print ('Title: ${myBook.getMediaTitle()}'); print ('Author: ${myBook.getBookAuthor()}'); print ('Type: ${myBook.getMediaType()}'); }

Обсуждение

В примере кода класс Media расширен за счет подкласса Book. В качестве родительского класса функциональность Media будет доступна любому дочернему классу, как показано на рисунке 5-4.

Рисунок 5-4. Наследование классов
Рисунок 5-4. Наследование классов

Наследование позволяет классам принимать методы и свойства суперкласса, как показано на рисунке 5-4. В результате дочерний и родительский классы поддерживают одни и те же свойства и методы.

Следовательно, как показано на рисунке 5-5, класс Book включает свойства и методы, связанные с носителями, в дополнение ко всему, что было явно определено в классе Book.

Рисунок 5-5. Отношение наследования классов
Рисунок 5-5. Отношение наследования классов

Мы создаем дочерний подкласс Book, который расширяет класс Media, что означает, что его можно использовать для доступа к методам и переменным, созданным в нем. При использовании подкласса можно переопределить существующие функциональные возможности класса, например, методы и т.д.

Extends обеспечивает наследование общего класса, при котором функциональность родительского (т.е. суперкласса) доступна дочернему (т.е. подклассу). Ключевое слово extends связывает родительский и дочерний классы один к одному, что означает, что множественное наследование не поддерживается. При использовании этой связи имейте в виду, что у вас, скорее всего, будет вызов super.method(), чтобы убедиться, что родительский класс знает об изменениях, внесенных в дочерний.

Примечание: мы не объявляем повторно методы setMediaTitle класса Book. Вместо этого мы можем вызвать этот метод из класса Book, как если бы он был явно объявлен.

Использование расширений является полезным подходом, когда доступны аналогичные структуры данных, для которых потенциально требуются немного разные методы. В примере класс Media представляет собой общую абстракцию, которая настроена для хранения базовой информации. Класс Book - это специализация класса Media, предлагающая возможность добавлять информацию, относящуюся к книге.

Добавление интерфейса класса

Проблема

Вы хотите использовать спецификацию класса, чтобы описать свойства и методы, которые должны быть объявлены при определении объекта.

Решение

Используйте интерфейс класса для определения спецификации объекта, которой должны придерживаться разработчики.

Вот пример того, как определить класс интерфейса в Dart:

abstract class Media { late String myId; late String myTitle; late String myType; void setMediaTitle(String mediaTitle); String getMediaTitle(); void setMediaType(String mediaType); String getMediaType(); void setMediaId(String mediaId); String getMediaId(); } class Book implements Media { @override late String myId; @override late String myTitle; @override late String myType; @override void setMediaTitle(String mediaTitle) { myTitle = mediaTitle; } @override String getMediaTitle() { return myTitle; } @override void setMediaType(String mediaType) { myType = mediaType; } @override String getMediaType() { return myType; } @override void setMediaId(String mediaId) { myType = mediaId; } @override String getMediaId() { return myId; } Book(String mediaTitle, String mediaType, String mediaId) { myTitle = mediaTitle; myType = mediaType; myId = mediaId; } } void main() { final Book myBook = Book("Serverless Computing with Google Cloud", "Book", "ISBN-1111"); print(myBook.getMediaTitle()); print(myBook.getMediaType()); print(myBook.getMediaId()); }

Обсуждение

В примере кода интерфейс класса Media используется подклассом Book. Media является родительским классом, поэтому его определения будут доступны любому дочернему классу. Обратите внимание на отсутствие реализации и инициализации, связанных с родительским классом. Вместо этого реализация остается на усмотрение пользователя интерфейса.

Чтобы использовать класс интерфейса, используйте ключевое слово implements. Если вы знакомы с другими языками, вы можете распознать термин абстрактный класс. Абстрактный класс предоставляет определение для класса, но не может использоваться для инициализации объекта.

В примере абстрактный класс с именем Media создает общий интерфейс для мультимедийной информации, как показано на рис. 5-6. Класс Book реализует интерфейс класса Media, что означает, что он отвечает за получение и установку значений, указанных в абстрактном классе. Как свойства, так и методы должны быть определены там, где они переопределяют значения, указанные в интерфейсе. Каждое значение, определенное в классе Book, которое определено в классе Media, имеет префикс @override, означающий, что интерфейс уже определен.

Рисунок 5-6. Интерфейс класса
Рисунок 5-6. Интерфейс класса

Как правило, абстрактный интерфейс будет использоваться для определения универсальных типов, которые оставляют реализацию разработчику подкласса, подлежащего определению. На рисунке 5-7 можно увидеть взаимосвязь между суперклассом и подклассом.

Рисунок 5-7. Класс реализует взаимосвязь
Рисунок 5-7. Класс реализует взаимосвязь

Подкласс может реализовывать несколько интерфейсов. Однако при создании подкласса следует помнить о том, что иерархия классов не должна быть чрезмерно сложной. Использование интерфейса класса требует, чтобы вы реализовали указанный интерфейс и соответствовали подписи, используемой абстрактным классом.

Типичный вариант использования определения интерфейса - это когда реализация будет рассматриваться как отдельная задача.

Добавление смешивания классов

Проблема

Вы хотите, чтобы существующий класс объединял функциональность из нескольких иерархий классов.

Решение

Используйте Mixins (Миксины), когда требуется функциональность от нескольких классов. Миксины являются мощным инструментом при работе с классами и позволяют объединять информацию из нескольких классов.

Вот пример использования миксина:

abstract class SnickersOriginal { bool hasHazelnut = true; bool hasRice = false; bool hasAlmond = false; } abstract class SnickersCrisp { bool hasHazelnut = true; bool hasRice = true; bool hasAlmond = false; } class ChocolateBar { bool hasChocolate = true; } class CandyBar extends ChocolateBar with SnickersOriginal { List<String> ingredients = []; CandyBar(){ if (hasChocolate){ ingredients.add('Chocolate'); } if (hasHazelnut){ ingredients.add('Hazelnut'); } if (hasRice){ ingredients.add('Rice'); } if (hasAlmond){ ingredients.add('Almonds'); } } List<String> getIngredients(){ return ingredients; } } void main() { var snickersOriginal = CandyBar(); print ('Ingredients:'); snickersOriginal.getIngredients().forEach((ingredient) => print(ingredient)); }

Обсуждение

В примере определены два абстрактных класса для хранения информации, относящейся к вариациям шоколадного батончика Snickers. Основной класс шоколадного батончика не содержит требуемой функциональности, поэтому мы включаем новый класс, чтобы расширить возможности программы. На рисунке 5-8 показано, как классы могут быть расширены с помощью ключевого слова mixin.

Рисунок 5-8. Расширение класса на основе <i>Mixin</i>
Рисунок 5-8. Расширение класса на основе Mixin

Ключевое слово with было введено в Dart совсем недавно; однако это то, о чем просили разработчики. Если вы работаете с игровым движком Flame, вы будете часто использовать это ключевое слово. Если вы работали с другими языками, возможно, вам больше знаком термин mixin, в котором несколько классов могут быть объединены для обеспечения дополнительной функциональности.

Mixin может использоваться, как с определениями наследования, так и с определениями интерфейсных классов. Чтобы использовать абстрактный класс с классом CandyBar, мы используем mixin. Mixin требует использования ключевого слова with и объединяет объекты класса.

На рисунке 5-9 суперклассы, на которые даны ссылки, должны оставаться изолированными, что означает, что используемые классы не должны перекрываться. Базовый класс CandyBar не должен переопределять конструктор по умолчанию, используемый в абстрактном или родительском классе.

Рисунок 5-9. Класс расширяет связь
Рисунок 5-9. Класс расширяет связь

Как правило, абстрактный класс используется для определения схемы создания объекта. В примере абстрактный класс обозначает ключевые ингредиенты шоколадного батончика. Кроме того, мы создаем класс шоколадного батончика, который можно использовать для хранения общих сведений.

Используйте mixin, чтобы позволить объекту подкласса включать больше функциональных возможностей без необходимости написания специального кода. В примере комбинация родительского класса с абстрактным классом позволяет объединить различные функциональные возможности. В результате этого слияния подкласс CandyBar наделен поведением, относящимся к плитке шоколада и разнообразным сникерсам (т.е. оригинальным по сравнению с версией Crisp). Как только дочерний подкласс создан, его можно использовать для доступа к общим ингредиентам CandyBar.

Начать дискуссию