Menu
Coddy logo textTech

Реализация против наследования

Часть раздела Объектно-ориентированное программирование путешествия по Dart на Coddy — урок 47 из 110.

Теперь, когда вы понимаете и extends, и implements, давайте проясним, когда использовать каждое из них.

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

class Animal {
  void breathe() {
    print('Breathing...');
  }
}

class Dog extends Animal {
  void bark() {
    print('Woof!');
  }
}

void main() {
  var dog = Dog();
  dog.breathe();  // Наследуется — работает автоматически
  dog.bark();
}

Используйте implements, когда вы хотите гарантировать структуру без наследования кода. Вы должны написать каждый метод самостоятельно:

class Robot implements Animal {
  @override
  void breathe() {
    print('Ventilating circuits...');  // Полностью кастомная реализация
  }
}

Ключевое отличие: extends говорит: «Я являюсь типом этого», в то время как implements говорит: «Я могу делать то же, что и это». Dog — это Animal, и она дышит так же. Robot не является животным, но он может выполнять тот же контракт, используя свою собственную логику.

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

challenge icon

Задание

Легко

Давайте создадим систему рабочих, которая продемонстрирует, когда использовать extends, а когда — implements. Вы создадите базовый класс рабочего, а затем покажете оба подхода: один класс, который действительно «является» рабочим и наследует поведение, и другой, который просто «может делать» то же, что и рабочий, но с полностью собственной логикой.

Вы организуете свой код в двух файлах:

  • worker.dart: Определите здесь иерархию рабочих:
    • Класс Worker со свойством String name и конструктором. Включите метод work(), который выводит '$name is working...', и метод takeBreak(), который выводит '$name takes a coffee break'
    • Класс OfficeWorker, который расширяет (extends) Worker. Добавьте свойство String department. Переопределите work(), чтобы он выводил '$name is typing reports in $department', но сохраните унаследованное поведение takeBreak()
    • Класс RobotWorker, который реализует (implements) Worker. Добавьте свойство String model. Поскольку роботы на самом деле не являются людьми-рабочими, но могут выполнять их функции, предоставьте полностью собственные реализации: work() должен выводить 'Robot $model is assembling parts', а takeBreak() должен выводить 'Robot $model is recharging'
  • main.dart: Импортируйте файл с рабочими и продемонстрируйте оба подхода:
    • Создайте OfficeWorker с именем 'Alice' в отделе 'Marketing'
    • Создайте RobotWorker с моделью 'RX-7'
    • Для офисного работника вызовите work(), а затем takeBreak()
    • Выведите пустую строку
    • Для робота-рабочего вызовите work(), а затем takeBreak()

Обратите внимание, как OfficeWorker автоматически наследует takeBreak(), потому что он расширяет Worker — он действительно «является» рабочим. В то же время RobotWorker должен предоставить собственную реализацию для всего, так как он только реализует интерфейс — он «может делать» то, что делает рабочий, но по-своему, по-роботизированному.

Ожидаемый вывод:

Alice is typing reports in Marketing
Alice takes a coffee break

Robot RX-7 is assembling parts
Robot RX-7 is recharging

Шпаргалка

Используйте extends для наследования поведения от родительского класса. Подкласс автоматически получает все методы родителя и может их переопределять:

class Animal {
  void breathe() {
    print('Breathing...');
  }
}

class Dog extends Animal {
  void bark() {
    print('Woof!');
  }
}

void main() {
  var dog = Dog();
  dog.breathe();  // Наследуется автоматически
  dog.bark();
}

Используйте implements, чтобы гарантировать структуру без наследования кода. Вы должны предоставить собственную реализацию для каждого метода:

class Robot implements Animal {
  @override
  void breathe() {
    print('Ventilating circuits...');
  }
}

Ключевые различия:

  • extends: «Я являюсь типом этого» — истинное наследование с общим кодом
  • implements: «Я могу делать то же, что и это» — обеспечивает соблюдение контракта с пользовательской логикой
  • Вы можете расширять только один класс, но реализовывать несколько интерфейсов

Выбирайте extends для истинных отношений «родитель-потомок», где поведение должно наследоваться. Выбирайте implements, когда вам нужна гибкость или вы хотите обеспечить соблюдение контракта без совместного использования реализации.

Попробуйте сами

import 'worker.dart';

void main() {
  // TODO: Создайте OfficeWorker по имени 'Alice' в отделе 'Marketing'
  
  // TODO: Создайте RobotWorker с моделью 'RX-7'
  
  // TODO: Для офисного работника вызовите work(), затем takeBreak()
  
  // TODO: Выведите пустую строку
  
  // TODO: Для робота-работника вызовите work(), затем takeBreak()
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование