December 7, 2024

ООП. Объектно-ориентированное программирование среднего уровня

Каждый разработчик должен понимать принципы ООП, чтобы проектировать качественный код. Эти принципы лежат в основе создания гибких и масштабируемых приложений. В статье мы подробно разберем каждый из принципов, проиллюстрируем их примерами и предложим решения тестовых задач с hh.ru для закрепления материала.

Вопрос 1

Какое из утверждений верно?

Варианты ответа:

  1. Объект — это функция, класс — это значение, возвращаемое этой функцией
  2. Класс — это шаблон, определяющий состояние и поведение объектов, объект — это экземпляр класса
  3. Объект — это шаблон, определяющий состояние и поведение классов, класс — это экземпляр объекта
  4. Класс — это функция, объект — это значение, возвращаемое этой функцией
  5. Класс — это имя переменной, объект — это значение, которое присваивается этой переменной

Обоснование:
Объектно-ориентированное программирование основывается на следующих ключевых понятиях:

  • Класс — это шаблон или схема, определяющая свойства (атрибуты) и методы (функции), которыми обладают объекты.
  • Объект — это конкретный экземпляр класса, который содержит данные и поведение, определенные в классе.

Разберем варианты:

  1. Объект — это функция, класс — это значение, возвращаемое этой функцией — неверно, поскольку объект и класс не имеют прямой связи с функцией, если не говорить о фабричных функциях.
  2. Класс — это шаблон, определяющий состояние и поведение объектов, объект — это экземпляр класса — верно, так как класс используется для создания объектов, а объект является конкретной реализацией класса.
  3. Объект — это шаблон, определяющий состояние и поведение классов, класс — это экземпляр объекта — неверно, так как объект не определяет классы, а классы создают объекты.
  4. Класс — это функция, объект — это значение, возвращаемое этой функцией — неверно, классы могут быть реализованы через функции в некоторых языках (например, JavaScript), но это не базовое определение.
  5. Класс — это имя переменной, объект — это значение, которое присваивается этой переменной — неверно, классы не являются просто переменными.

📌Правильный ответ:
2. Класс — это шаблон, определяющий состояние и поведение объектов, объект — это экземпляр класса

Вопрос 2

Что из перечисленного — пример классического антипаттерна в ООП?

Варианты ответа:

  1. Использование условных операторов с множественным ветвлением
  2. Использование индексации массива за пределами его диапазона
  3. Использование класса с чрезмерно большим количеством методов, имеющих разное предназначение
  4. Использование большого количества вложенных циклов в коде
  5. Использование чрезмерно длинных имен методов в классах

Обоснование:
Антипаттерны в ООП описывают плохо спроектированные структуры или решения, которые снижают читаемость, поддерживаемость и эффективность кода.

Разберем варианты:

  1. Использование условных операторов с множественным ветвлением — это проблема структурного программирования, а не ООП.
  2. Использование индексации массива за пределами его диапазона — это ошибка реализации, а не антипаттерн проектирования.
  3. Использование класса с чрезмерно большим количеством методов, имеющих разное предназначение — это антипаттерн под названием God Object или Big Ball of Mud. Такие классы слишком сложны, нарушают принцип единственной ответственности (SRP) и затрудняют тестирование и поддержку.
  4. Использование большого количества вложенных циклов в коде — это проблема алгоритмической сложности, а не антипаттерн ООП.
  5. Использование чрезмерно длинных имен методов в классах — это влияет на читаемость, но не относится к классическим антипаттернам.

📌Правильный ответ:
3. Использование класса с чрезмерно большим количеством методов, имеющих разное предназначение

Вопрос 3

Какое из утверждений верно?

Варианты ответа:

  1. Абстракция и инкапсуляция — это синонимичные понятия
  2. Абстракция может достигаться путем использования интерфейсов, а инкапсуляция — за счет модификаторов доступа
  3. Принцип абстракции относится к объектно-ориентированному программированию, а инкапсуляции — к процедурному
  4. Переопределение метода — это прием реализации инкапсуляции, а виртуальные методы — прием реализации абстракции
  5. Абстракция — это конкретный механизм реализации принципа инкапсуляции

Обоснование:
Разберем ключевые понятия:

  • Абстракция: это процесс выделения общих характеристик объектов, сокрытие деталей реализации и работа только с необходимыми аспектами. Например, это может быть реализовано через интерфейсы или абстрактные классы.
  • Инкапсуляция: это механизм сокрытия деталей реализации и управления доступом к данным с помощью модификаторов доступа (private, protected, public).

Рассмотрим варианты:

  1. Абстракция и инкапсуляция — это синонимичные понятия — неверно, это разные принципы с разными целями: абстракция фокусируется на общем, а инкапсуляция — на сокрытии деталей.
  2. Абстракция может достигаться путем использования интерфейсов, а инкапсуляция — за счет модификаторов доступа — верно, это корректно описывает механизмы реализации абстракции и инкапсуляции.
  3. Принцип абстракции относится к объектно-ориентированному программированию, а инкапсуляции — к процедурному — неверно, оба принципа относятся к ООП.
  4. Переопределение метода — это прием реализации инкапсуляции, а виртуальные методы — прием реализации абстракции — неверно, так как переопределение связано с полиморфизмом, а виртуальные методы — с поддержкой полиморфизма.
  5. Абстракция — это конкретный механизм реализации принципа инкапсуляции — неверно, абстракция и инкапсуляция реализуются разными средствами.

📌Правильный ответ:
2. Абстракция может достигаться путем использования интерфейсов, а инкапсуляция — за счет модификаторов доступа

Вопрос 4

Что из перечисленного — пример реализации статического полиморфизма?

Варианты ответа:

  1. Когда необходимо создать утилитарный класс — класс-помощник, содержащий статические переменные и статические методы
  2. Когда необходимо, чтобы производные классы могли переопределять методы базового класса
  3. Когда в классе есть несколько методов, имеющих одинаковое имя, но разное число параметров одного типа
  4. Когда базовый класс имеет виртуальные методы, а дочерние классы переопределяют эти методы
  5. Во всех вышеперечисленных случаях

Обоснование:
Статический полиморфизм (compile-time polymorphism) достигается за счет перегрузки методов или операторов. Это означает, что в классе можно иметь несколько методов с одинаковым именем, но с разными наборами параметров (количество или тип). Это позволяет определить, какой метод вызывать, на этапе компиляции.

Разберем варианты:

  1. Когда необходимо создать утилитарный класс... — это связано с организацией классов и не имеет отношения к полиморфизму.
  2. Когда необходимо, чтобы производные классы могли переопределять методы базового класса — это пример динамического полиморфизма (runtime polymorphism), реализуемого через виртуальные методы.
  3. Когда в классе есть несколько методов, имеющих одинаковое имя, но разное число параметров одного типа — это именно статический полиморфизм, так как метод определяется на этапе компиляции.
  4. Когда базовый класс имеет виртуальные методы... — это пример динамического полиморфизма.
  5. Во всех вышеперечисленных случаях — неверно, так как только третий вариант описывает статический полиморфизм.

📌Правильный ответ:
3. Когда в классе есть несколько методов, имеющих одинаковое имя, но разное число параметров одного типа

Вопрос 5

В каком из перечисленных случаев соблюден принцип единственной ответственности?

Варианты ответа:

  1. Класс Order содержит методы для работы с оплатой, доставкой и управлением статусом заказа
  2. Класс OrderStatusControl содержит методы для управления статусом заказа
  3. Класс PaymentDelivery содержит методы для работы с оплатой и доставкой заказа
  4. Класс Payment содержит методы для работы с оплатой заказа, а класс DeliveryOrderStatusControl — для работы с доставкой и управлением статусом заказа
  5. Класс Order содержит основной метод для работы с доставкой заказа и дополнительные методы — для управления статусом заказа

Обоснование:
Принцип единственной ответственности (Single Responsibility Principle, SRP) из SOLID утверждает, что класс должен иметь только одну причину для изменения, то есть быть ответственным за одну конкретную задачу.

Разберем варианты:

  1. Класс Order содержит методы для работы с оплатой, доставкой и управлением статусом заказа — нарушает SRP, так как класс выполняет несколько задач: управление оплатой, доставкой и статусом.
  2. Класс OrderStatusControl содержит методы для управления статусом заказа — соблюдает SRP, так как класс выполняет только одну задачу: управление статусом заказа.
  3. Класс PaymentDelivery содержит методы для работы с оплатой и доставкой заказа — нарушает SRP, так как класс объединяет две задачи: оплату и доставку.
  4. Класс Payment содержит методы для работы с оплатой заказа, а класс DeliveryOrderStatusControl — для работы с доставкой и управлением статусом заказа — нарушает SRP, так как DeliveryOrderStatusControl объединяет задачи доставки и управления статусом.
  5. Класс Order содержит основной метод для работы с доставкой заказа и дополнительные методы — для управления статусом заказа — нарушает SRP, так как объединяет две задачи: доставку и статус заказа.

📌Правильный ответ:
2. Класс OrderStatusControl содержит методы для управления статусом заказа

Вопрос 6

В каком случае НЕ выполняется блок finally при обработке исключений?

Варианты ответа:

  1. Если возникло исключение в блоке try, но оно не было обработано
  2. Если возникло исключение в блоке try, и оно было обработано
  3. Если возникло исключение в блоке catch
  4. Если программа была принудительно завершена
  5. Если исключение не возникло

Обоснование:
Блок finally предназначен для выполнения кода, который должен быть выполнен независимо от того, произошло исключение или нет. Обычно он используется для освобождения ресурсов, закрытия файлов, подключения к базе данных и т. д.

Когда блок finally выполняется:

  • При отсутствии исключений.
  • Если исключение произошло и было обработано (catch).
  • Если исключение произошло, но не было обработано.

Когда блок finally НЕ выполняется:

  • Если программа была принудительно завершена (например, вызовом System.exit() в Java или аналогичных методов).

Разберем варианты:

  1. Если возникло исключение в блоке try, но оно не было обработано — блок finally выполняется.
  2. Если возникло исключение в блоке try, и оно было обработано — блок finally выполняется.
  3. Если возникло исключение в блоке catch — блок finally выполняется, так как он запускается после завершения обработки в блоке catch.
  4. Если программа была принудительно завершена — блок finally НЕ выполняется, так как программа завершает свое выполнение немедленно.
  5. Если исключение не возникло — блок finally выполняется.

📌Правильный ответ:
4. Если программа была принудительно завершена

Вопрос 7

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

Варианты ответа:

  1. Только private
  2. Можно использовать любой модификатор
  3. Только public
  4. Protected или private
  5. Только protected

Обоснование:
Разберем модификаторы доступа:

  • private: Поля и методы доступны только внутри самого класса. Они недоступны из производного класса, поэтому этот модификатор не подходит.
  • public: Поля и методы доступны из любого места программы, что нарушает указанное требование.
  • protected: Поля и методы доступны внутри самого класса и его производных классов, но недоступны из внешнего кода. Это полностью соответствует задаче.

Другие варианты:

  • Можно использовать любой модификатор — неверно, так как требования явно исключают использование public и private.
  • Protected или private — частично неверно, так как использование private нарушит требование доступности из производного класса.

📌Правильный ответ:
5. Только protected

Вопрос 9

Вы разрабатываете систему для управления грузоперевозками. У вас есть базовый класс Transport, от которого наследуются классы Truck и Ship. В классе Transport есть метод calculateCost(), который рассчитывает стоимость доставки.

При использовании этого метода в классе Ship возникает ошибка, и программа перестает работать. Какой принцип ООП мог быть нарушен при разработке этой системы?

Варианты ответа:

  1. Принцип открытости/закрытости
  2. Принцип подстановки Барбары Лисков
  3. Принцип инверсии зависимостей
  4. Принцип разделения интерфейса
  5. Принцип единственной ответственности

Обоснование:
Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP) гласит, что любой объект класса-наследника должен быть полностью заменяем объектом базового класса без нарушения логики программы.

Если при использовании метода calculateCost() в классе Ship возникает ошибка, это означает, что класс Ship не соответствует ожиданиям, заложенным в базовом классе Transport. Возможно, метод в классе-наследнике реализован с нарушением, либо метод базового класса не учел специфику наследников.

Другие варианты:

  1. Принцип открытости/закрытости — относится к модификации кода, но не к корректной работе наследников.
  2. Принцип инверсии зависимостей — касается проектирования зависимостей между классами, но не поведения наследников.
  3. Принцип разделения интерфейса — касается дробления интерфейсов на специализированные, что не относится к ситуации.
  4. Принцип единственной ответственности — относится к тому, чтобы класс выполнял только одну задачу, что здесь не является проблемой.

📌Правильный ответ:
2. Принцип подстановки Барбары Лисков

Вопрос №19:

В какой из ситуаций корректно применить абстрактные классы, а не интерфейсы?

Варианты ответа:

  1. Когда необходимо уменьшить связанность кода
  2. Когда нужно создать разноплановые по состоянию классы, но с общим действием (методом)
  3. Когда нужно использовать множественное наследование
  4. Когда необходимо хранить общее состояние классов-наследников
  5. Во всех перечисленных выше случаях

Обоснование:
Абстрактные классы применяются, когда необходимо:

  • Реализовать общий функционал для группы классов с возможностью изменения или добавления методов;
  • Хранить общее состояние (поля, переменные) для всех классов-наследников;
  • Обеспечить наследование общего поведения, оставив часть функционала для реализации в подклассах.

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

Рассмотрим варианты:

  1. Когда необходимо уменьшить связанность кода — абстрактные классы уменьшают связанность, так как позволяют переиспользовать общий функционал в наследниках.
  2. Когда нужно создать разноплановые по состоянию классы, но с общим действием (методом) — абстрактный класс позволяет объединить общие методы и свойства, реализуя конкретное поведение в подклассах.
  3. Когда нужно использовать множественное наследование — это спорное утверждение, так как в языках вроде Java множественное наследование классов запрещено, но допускается множественная реализация интерфейсов. Однако абстрактные классы могут быть полезны для частичного решения этой задачи.
  4. Когда необходимо хранить общее состояние классов-наследников — это ключевое преимущество абстрактных классов перед интерфейсами, которые не позволяют хранить состояние.

📌Правильный ответ:
5. Во всех перечисленных выше случаях

Вопрос 10

У вас есть иерархия классов для работы с фигурами: базовый класс Figure и два его дочерних класса, Rectangle и Circle. Класс Rectangle имеет дополнительный метод get_area(), который возвращает площадь прямоугольника.

В языках со строгой типизацией, каким образом можно использовать приведение типов для получения площади прямоугольника, используя метод get_area()?

Варианты ответа:

  1. Создать объект класса Figure, передав в качестве аргумента объект класса Rectangle, и вызвать метод get_area() на объекте класса *Figure`
  2. Приведение типов в данном случае невозможно
  3. Создать объект класса Rectangle, передав в качестве аргумента объект класса Figure, и вызвать метод get_area() на объекте класса *Rectangle`
  4. Привести объект класса Figure к типу Rectangle и вызвать метод get_area() на объекте класса *Rectangle`
  5. Привести объект класса Rectangle к типу Figure и вызвать метод get_area() на объекте класса *Figure`

Обоснование:
Приведение типов в языках со строгой типизацией, таких как Java или C++, возможно, если объект действительно является экземпляром производного класса. Если у вас есть объект базового класса (Figure), который фактически является экземпляром Rectangle, вы можете привести его к типу Rectangle и использовать специфичный метод get_area().

Разберем варианты:

  1. **Создать объект класса Figure, передав в качестве аргумента объект класса Rectangle, и вызвать метод get_area() на объекте класса *Figure** — невозможно, так как метод get_area()` не определен в базовом классе Figure.
  2. Приведение типов в данном случае невозможно — неверно, приведение типов возможно, если объект на самом деле является экземпляром Rectangle.
  3. *Создать объект класса Rectangle, передав в качестве аргумента объект класса Figure, и вызвать метод get_area() на объекте класса Rectangle` — это неверно, так как Figure не может быть приведен к Rectangle.
  4. *Привести объект класса Figure к типу Rectangle и вызвать метод get_area() на объекте класса Rectangle` — верно, если объект Figure фактически является экземпляром Rectangle, такое приведение возможно.
  5. **Привести объект класса Rectangle к типу Figure и вызвать метод get_area() на объекте класса *Figure** — неверно, так как метод get_area()` недоступен для типа Figure.

📌Правильный ответ:
4. Привести объект класса Figure к типу Rectangle и вызвать метод get_area() на объекте класса *Rectangle`.

Вопрос 11

Параметрический полиморфизм осуществляется, в первую очередь, через использование...

Варианты ответа:

  1. Множественного наследования
  2. Виртуальных методов
  3. Ковариантности
  4. Обобщенных классов
  5. Сужения класса

Обоснование:
Параметрический полиморфизм — это способность функции или класса работать с различными типами данных, при этом тип определяется параметром. Он реализуется с использованием обобщений (generics). Например, в языках программирования, таких как Java, C++, и C#, для реализации параметрического полиморфизма используются обобщенные классы и методы.

Разберем варианты:

  1. Множественное наследование — это относится к структурной организации классов и не связано с параметрическим полиморфизмом.
  2. Виртуальные методы — это механизм реализации динамического полиморфизма, а не параметрического.
  3. Ковариантность — это относится к совместимости типов при наследовании, но не напрямую к параметрическому полиморфизму.
  4. Обобщенные классы — это правильный ответ, так как именно обобщения (generics) позволяют работать с параметрическим полиморфизмом.
  5. Сужение класса — это не относится к параметрическому полиморфизму.

📌Правильный ответ:
4. Обобщенных классов

Вопрос 12

У вас есть модули, которые зависят друг от друга: если вы меняете один модуль, вы должны внести изменения в зависимые модули.

Какой термин используется для описания этой проблемы?

Варианты ответа:

  1. Проблема модульности
  2. Проблема связанности
  3. Проблема сопряжения
  4. Проблема полиморфизма
  5. Проблема иерархии модулей

Обоснование:
Сопряжение (coupling) — это мера зависимости между модулями. Чем выше сопряжение, тем больше один модуль зависит от другого. Высокая степень сопряжения приводит к тому, что изменение одного модуля требует изменения других зависимых модулей. Это нарушает принципы модульности и усложняет поддержку и тестирование системы.

Разберем варианты:

  1. Проблема модульности — модульность связана с разделением системы на независимые модули, но здесь обсуждается не модульность, а взаимозависимость.
  2. Проблема связанности — связанность (cohesion) касается внутренней согласованности модуля, а не зависимости между модулями.
  3. Проблема сопряжения — верно, так как это определение описывает зависимость между модулями.
  4. Проблема полиморфизма — не относится к обсуждаемой ситуации, так как полиморфизм связан с реализацией методов в ООП.
  5. Проблема иерархии модулей — это не совсем точный термин, так как проблема заключается именно в зависимости, а не в иерархической организации.

📌Правильный ответ:
3. Проблема сопряжения

👉🏻Навигация и ссылки по всем материалам в Telegram