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

«Что такое абстракция в ООП?» — вопрос из категории ООП, который задают на 31% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Простыми словами: Я создаю "черный ящик". Пользователь знает, что этот ящик делает (например, отправляет уведомление), но не знает и не должен знать, как именно он это делает (через email, SMS или Slack).

Как я это применяю на практике:

  1. С помощью абстрактных классов и интерфейсов определяю контракт (набор методов), который должны реализовать конкретные классы.
  2. Клиентский код зависит от абстракции (интерфейса), а не от конкретной реализации. Это основа для соблюдения принципа DIP (Dependency Inversion).

Пример из реального проекта: Система логирования. Вместо того чтобы жестко зависеть от конкретной библиотеки логирования (например, Monolog) во всем коде, я создаю абстракцию.

// Абстракция - что мы можем делать с логгером?
interface LoggerInterface {
    public function info(string $message, array $context = []): void;
    public function error(string $message, array $context = []): void;
}

// Конкретная реализация, скрывающая детали работы с Monolog
class MonologLoggerAdapter implements LoggerInterface {
    private MonologLogger $logger;

    public function __construct() {
        $this->logger = new MonologLogger('app');
        $handler = new MonologHandlerStreamHandler('path/to/your.log');
        $this->logger->pushHandler($handler);
    }

    public function info(string $message, array $context = []): void {
        $this->logger->info($message, $context);
    }

    public function error(string $message, array $context = []): void {
        $this->logger->error($message, $context);
    }
}

// Сервис в приложении, который использует логгер. Он зависит только от интерфейса.
class OrderService {
    public function __construct(private LoggerInterface $logger) {}

    public function processOrder(Order $order): void {
        try {
            // ... логика обработки заказа ...
            $this->logger->info('Order processed', ['order_id' => $order->id]);
        } catch (Exception $e) {
            $this->logger->error('Order processing failed', ['error' => $e->getMessage()]);
            throw $e;
        }
    }
}

Преимущества такого подхода:

  • Снижение связанности: OrderService не знает о Monolog.
  • Гибкость: Завтра я могу легко заменить Monolog на другую библиотеку или отправить логи в Sentry, создав новый адаптер SentryLoggerAdapter. Мне нужно будет изменить только конфигурацию внедрения зависимостей, а код OrderService останется прежним.
  • Упрощение тестирования: Я могу подставить заглушку (mock) LoggerInterface в юнит-тестах для OrderService.

Видео-ответы