Перейти к основному содержимому

Абстракция - скрытие деталей реализации

Разработчику Аналитику Тестировщику
Архитектору Инженеру


Абстракция

Что такое абстракция?

Абстракция — выделение существенных характеристик объекта и отделение их от деталей реализации. Позволяет работать с объектами, опираясь на контракт "что делает", без знания "как устроено внутри". Например, водитель нажимает педаль газа, не разбирая устройство двигателя.

В энциклопедической трактовке абстракция в ООП отвечает на вопрос "что?", оставляя "как?" внутри класса или модуля.

Абстракция данных (АДТ)

Абстракция данных — одно из старейших понятий, связанных с ООП: тип данных связывается с набором операций, а внутреннее представление скрыто. Пользователь типа вызывает только разрешённые операции; реализацию можно менять, если контракт сохранён.

УровеньПример
АДТстек: push, pop, top без раскрытия массива или списка
Класс в ООПСчёт: Пополнить, Списать, Баланс
ИнтерфейсПечатаемый: Печать() без полей

Класс в ООП — практическая форма АДТ с возможностью наследования и полиморфизма (введение, инкапсуляция).

Play ITЗагрузка интерактивного демо…


Абстракция - скрытие деталей реализации

Зачем нужна абстракция? В чём смысл?

  1. Упрощение сложных систем. Можно разбить сложные системы на понятные части. К примеру, мы в реальном мире не думаем о том, как работает двигатель автомобиля, чтобы управлять, нам хватит понимать, где педали, а где руль. В программировании можно так же спрятать внутрь реализацию.
  2. Сокрытие ненужных деталей. К примеру, другому программисту не нужно знать, как работает метод "мяукнуть()", ему достаточно лишь понимать, что он делает. Понятно, что мяукание издаст определённый звук. Но какие будут происходить процессы внутри "кота" - не нужно нам знать.
  3. Уменьшение сложности кода. Абстракция помогает структурировать код, делая его:
    • читаемым (логика разбита на модули);
    • поддерживаемым (изменения внутри класса не ломают внешний код);
    • масштабируемым (можно добавлять новые функции, не переписывая всё).
  4. Унификация взаимодействия. Абстракция позволяет стандартизировать работу с объектами. Допустим, в игре разные персонажи могут иметь метод attack(), но реализация у мечника и мага будет отличаться.

Как работает абстракция?

Абстракция достигается за счёт:

  • абстрактных классов;
  • абстрактных методов;
  • сокрытия данных.

image-4.png


Абстрактный класс

Абстрактный класс – это класс, который нельзя создать напрямую – только через наследование. Содержит абстрактные методы и/или обычные методы.

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

Нельзя создавать экземпляр абстрактного класса, но если сделать неабстрактного наследника, то можно уже создавать объекты.

АБСТРАКТНЫЙ КЛАСС Транспорт
метод Переместиться() // без тела — обязан реализовать наследник
КОНЕЦ

КЛАСС Автомобиль НАСЛЕДУЕТ Транспорт
метод Переместиться()
// конкретная логика езды
конец
КОНЕЦ

// new Транспорт() — запрещено
машина := новый Автомобиль()

Справочно на Java

Код ITЗагрузка примера кода…

Transport – абстрактный класс с:

  • абстрактным методом move();
  • обычным методом honk().

Car и Airplane – наследники, которые обязаны реализовать move().

И нельзя создать Transport напрямую:

Transport t = new Transport(); // Ошибка! Transport абстрактный.

Абстрактный метод

Абстрактный метод – это метод без реализации, который обязаны переопределить все дочерние классы. Принцип тот же – это просто указание, что абстрактный класс содержит этот метод, но как работает метод – не указано. Так, программист реализует критичную логику, и использует унифицированный интерфейс для разных классов. Словом, будет порядок. Пример:

Код ITЗагрузка примера кода…


Интерфейс

Интерфейс — это контракт (соглашение о том, какие операции должен уметь объект). В коде контракт записывается списком методов: имя, параметры, что возвращает метод. Класс, который берёт на себя этот контракт, реализует интерфейс — в Java и Kotlin ключевое слово implements, в C# после имени класса перечисляют интерфейсы через запятую.

Важные термины:

  • Реализация — конкретный код метода в классе. Интерфейс говорит "метод должен быть", класс показывает, как он работает.
  • Множественная реализация — один класс может реализовать несколько интерфейсов сразу (например, и "печатаемый", и "сохраняемый в файл"). Наследовать при этом можно только один класс — см. Наследование.
  • Метод по умолчанию (default method) — метод с готовым телом прямо в интерфейсе. Появился в Java 8 и C# 8, чтобы расширять API без поломки старых классов. Подробнее — ООП в Java, ООП в C#.

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

Пример:

Код ITЗагрузка примера кода…

Ещё один пример:

Код ITЗагрузка примера кода…

Сравнение с абстрактным классом — в разделе ниже.


Абстрактный класс и интерфейс

Абстрактный класс и интерфейс оба помогают спрятать детали и задать контракт — список обязательных операций. Разница в том, какие типы они объединяют и что ещё можно положить в базовый тип кроме сигнатур методов.

Связанные темы:

Термины, которые встретятся дальше

  • Отношение "является" (is-a, читается "из-а") — стиральная машина является бытовой техникой. Для таких связей берут абстрактный класс и наследование.
  • Отношение "умеет" (can-do, "кан-ду") — и орёл, и самолёт умеют летать, хотя самолёт птицей не является. Для общего поведения без общего предка берут интерфейс.
  • Состояние объекта — данные в полях (серийный номер, баланс, имя). Меняется у каждого экземпляра отдельно.
  • Базовый (родительский) класс — тип, от которого наследуются другие. Производный (дочерний) класс — наследник, дополняющий или переопределяющий поведение базы.

Наследование и несколько контрактов

В Java, C#, Kotlin, TypeScript и ряде других языков действует правило:

  • от класса (в том числе абстрактного) наследуют не больше одного;
  • интерфейсов на один класс может быть сколько угодно.

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

КЛАСС СтиральнаяМашина НАСЛЕДУЕТ БытоваяТехника
РЕАЛИЗУЕТ ПодключаемыйКWiFi, Энергопотребляемый

В C++ формально допускают наследование от нескольких классов; роль узкого контракта часто играет базовый класс только с чисто виртуальными методами. В Go классов нет — есть структуры и интерфейсы.

Кого объединяет каждый механизм

Абстрактный класс связывает родственные типы одной предметной области.

  • Пример: Bird (птица) — общий предок для Eagle (орёл) и Penguin (пингвин).
  • Оба наследника разделяют идею "это птица", даже если орёл летает, а пингвин — нет.

Интерфейс связывает любые типы с одинаковым поведением.

  • Пример: Flyable (летающий) реализуют и Eagle, и Airplane (самолёт).
  • Летать умеют оба; в одну иерархию "птиц" самолёт не входит.
ВопросАбстрактный классИнтерфейс
Кого объединяетРодственные типы одной иерархииЛюбые типы с общим поведением
Типичный вопрос при проектировании"Что это за сущность?""Что объект умеет делать?"
Англоязычная меткаis-a ("является")can-do ("умеет")

Состояние и реализация методов

Абстрактный класс может хранить поля (переменные экземпляра), конструктор и методы с уже написанным телом. Дочерние классы переиспользуют этот код и при необходимости дополняют защищённые (protected) члены базы.

Интерфейс обычно описывает только сигнатуры методов. В Java поля в интерфейсе по умолчанию становятся public static finalконстанты, одинаковые для всех реализаций, без отдельного состояния у каждого объекта. Если нескольким реализациям нужны общие изменяемые данные, их выносят в абстрактный класс или отдельный вспомогательный тип.

Сводная таблица

КритерийАбстрактный классИнтерфейс
СмыслЧасть одного семейства типов (is-a)Общая способность или роль (can-do)
Сколько можно указать у классаОдин базовый классНесколько интерфейсов
Поля экземпляраДа, с любыми модификаторами доступаОбычно только константы
КонструкторЕстьНет
Готовый код в методахЛюбые методы, в том числе абстрактные без телаОбъявления; с Java 8 / C# 8 — ещё методы по умолчанию
Типичное применениеОбщая логика и поля для наследниковКонтракт для полиморфного кода и подмены реализации в тестах

Пример — умный дом

Разберём две сущности из системы умного дома.

Бытовая техника (HouseholdAppliance) — абстрактный класс:

  • хранит серийный номер и производителя;
  • задаёт конструктор для инициализации;
  • может содержать общий метод вроде turnOn() / Включить().

От него наследуют WashingMachine (стиральная машина) и Refrigerator (холодильник) — оба являются бытовой техникой.

Подключение к Wi‑Fi (WiFiConnectable) — интерфейс:

  • требует метод connectToNetwork() / ПодключитьКСети();
  • не задаёт, "что это за предмет", только способность выйти в сеть.

Его реализуют и стиральная машина, и SmartBulb (умная лампочка). Лампочка не входит в иерархию бытовой техники, при этом умеет подключаться к Wi‑Fi.

АБСТРАКТНЫЙ КЛАСС БытоваяТехника
поля: серийный_номер, производитель
конструктор(серийный_номер, производитель)
метод Включить() …
КОНЕЦ

КЛАСС СтиральнаяМашина НАСЛЕДУЕТ БытоваяТехника
КЛАСС Холодильник НАСЛЕДУЕТ БытоваяТехника

ИНТЕРФЕЙС ПодключаемыйКWiFi
метод ПодключитьКСети()
КОНЕЦ

КЛАСС СтиральнаяМашина РЕАЛИЗУЕТ ПодключаемыйКWiFi
КЛАСС УмнаяЛампочка РЕАЛИЗУЕТ ПодключаемыйКWiFi

В библиотеках часто встречается связка:

  • интерфейс Repository — что должен уметь любой репозиторий;
  • абстрактный класс AbstractRepository — общий код, который не хочется копировать в каждом наследнике.

Это перекликается с принципом инверсии зависимостей и паттерном "Шаблонный метод".

Как записано в разных языках

ЯзыкСтатья
JavaООП в Java
C#ООП в C#
PythonООП в Python (abc, Protocol)
KotlinООП в Kotlin
C++ООП в C++
TypeScriptООП в TypeScript
PHPООП в PHP
Goструктуры и интерфейсы

Полный маршрут по разделу — ООП, о разделе.


Где применяется абстракция

  • Библиотеки и фреймворки — в ORM вызываете save(), не собирая SQL вручную.
  • Игры — базовый Enemy с методом attack(), у Dragon и Zombie своя реализация.
  • GUIButton.click() скрывает отрисовку и обработку событий.
  • Платёжные API — единый метод оплаты при разных провайдерах (интеграции).