В каких случаях в ООП следует использовать интерфейс?

Ответ

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

Основные случаи применения:

  1. Определение полиморфного поведения. Несколько классов могут по-разному реализовать один интерфейс.

    interface PaymentProcessor {
        void processPayment(BigDecimal amount);
    }
    
    class CreditCardProcessor implements PaymentProcessor { /*...*/ }
    class PayPalProcessor implements PaymentProcessor { /*...*/ }
  2. Соблюдение принципа инверсии зависимостей (DIP). Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций (интерфейсов).
  3. Создание слабосвязанной архитектуры. Код зависит от интерфейса, а не от конкретной реализации, что упрощает замену и тестирование (например, внедрение мок-объектов в unit-тестах).
  4. Моделирование различных ролей объекта. Класс может реализовывать несколько интерфейсов.
    class Smartphone implements Camera, Phone, GPSNavigator { /*...*/ }
  5. Предоставление API для библиотек или фреймворков. Позволяет клиентам предоставлять свою реализацию (стратегия, наблюдатель).

Интерфейс vs. Абстрактный класс:

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

Ответ 18+ 🔞

А, интерфейсы, ёпта! Ну, это как договор, который ты подписываешь, а потом, блядь, выполняешь, иначе тебе пизда. Представь, что ты приходишь в зал — там висит бумажка: «Все, кто тут качается, должны уметь жать лежа и не срать под штангу». Вот это и есть интерфейс GymDude. Ты можешь быть хоть качком, хоть дрыщом — но если зашел, будь добр, контракт исполни.

Зачем это, блядь, нужно, спросишь? Да овердохуища причин!

  1. Полиморфизм, или «Одна дырка, а вставлять можно разное». Объявил ты интерфейс PaymentProcessor — «плати, сука». И пофиг, кто именно платит: кредитка, крипта или мешок картошки. Главное — метод processPayment вызвать.

    interface PaymentProcessor {
        void processPayment(BigDecimal amount); // Контракт: «Дай денег, блядь»
    }
    
    class CreditCardProcessor implements PaymentProcessor {
        public void processPayment(BigDecimal amount) {
            // Тут магия с полосочкой и CVV
        }
    }
    class PotatoBagProcessor implements PaymentProcessor {
        public void processPayment(BigDecimal amount) {
            // Взвешиваем мешок, ебать его в сраку
        }
    }
  2. Принцип «Зависеть от абстракций, а не от конкретных мудаков» (DIP). Твой крутой бизнес-логический модуль не должен знать, что там CreditCardProcessor внутри гонит. Он знает только абстракцию — интерфейс PaymentProcessor. Захотел поменять платёжку — просто подсунул другую реализацию, и ни один класс не охуел.

  3. Слабая связность, или «Живи сам и давай жить другим». Код, который использует интерфейс, нихрена не знает о внутренних пиздюлях конкретного класса. Это как договориться «принеси пиво» — тебе похуй, куда человек за ним бежал: в ларёк, на завод или у соседа спиздил. Главное — пиво в руках. Тестировать — вообще сказка: подсовываешь мок-объект, который просто делает return true, и не надо ебаться с реальной картой.

  4. Моделирование ролей, или «Я и швец, и жнец, и на хуя-баба яица». Один класс может реализовывать кучу интерфейсов. Смартфон — он и Camera, и Phone, и BrickForWindow в случае чего.

    class MySmartphone implements Camera, Phone, GPSNavigator, Paperweight {
        // Реализуй всё, жопа с ручками!
    }
  5. Библиотекам и фреймворкам — чтобы ты, пользователь, мог свою хуйню встроить. Объявили они интерфейс EventListener — а ты уже свою реализацию пишешь, где при событии «клик» не просто логируется, а запускается ракета. Стратегия, наблюдатель — вся эта архитектурная хуета держится на интерфейсах.

А теперь, блядь, главный вопрос: интерфейс или абстрактный класс?

  • Интерфейс — это когда ты говоришь: «ВСЕ, кто хочет быть Swimmable, ДОЛЖНЫ уметь swim(). Будь ты уткой, дедом Мазаем или кирпичом — плыви, сука». Определяет контракт для кого угодно.
  • Абстрактный класс — это когда у тебя есть общая родня. Допустим, есть абстрактный класс Canine (Псовые). У него уже есть реализованные методы breathe() и shitInTheYard(). А ты от него наследуешь Dog и Wolf и добавляешь своё: один barkAtMailman(), другой eatGrandma(). То есть даёшь общую базу для родственных уёбков.

Короче, интерфейс — это «что делать», а абстрактный класс часто ещё и «как делать часть этого». Выбирай, исходя из того, хочешь ли ты навязать только правила игры или ещё и часть игрового поля с декорациями.