В каких случаях требуется подменить один класс другим в Java
Что проверяют
Понимаете ли вы, зачем код строят вокруг абстракций и как на практике меняют реализацию без правки клиентов. Следом обычно спрашивают про принцип подстановки Лисков или про то, как в Spring выбрать один из нескольких бинов одного типа.
Как отвечать
Подмена нужна, когда у одной роли несколько реализаций или реализация зависит от окружения. Основные случаи:
- поведение по условию: паттерн Strategy, например интерфейс
PaymentProviderс реализациями под разные платёжные системы и выбором по настройке или по запросу; - тесты: реальный клиент внешнего API или репозиторий заменяются моком Mockito или in-memory реализацией. В Mockito 5 по умолчанию работает inline mock maker, поэтому мокаются и
finalклассы. В Spring-тестах бин подменяется через@MockitoBean, а@MockBeanс Spring Boot 3.4 помечен устаревшим; - окружение: разные бины под профили через
@Profile("dev"), выбор среди кандидатов через@Primaryи@Qualifier, запасная реализация через@ConditionalOnMissingBean; - плагины через SPI:
ServiceLoaderнаходит реализацию по файлу вMETA-INF/services, так подключаются JDBC-драйверы и провайдеры логирования; - чужой код, который нельзя менять: его оборачивают в Adapter или Decorator и подставляют обёртку, например с кэшем или повторами.
Условие любой подмены: клиент зависит от интерфейса и получает зависимость снаружи, через конструктор. Если класс сам делает new HttpClientImpl(), заменить реализацию без правки кода не выйдет.
Отдельно назовите принцип Лисков: наследник обязан выполнять контракт базового типа. Иначе подмена компилируется, но ломает логику.
Где спотыкаются
Нарушают контракт. Классика: List.of() реализует List, но add бросает UnsupportedOperationException, и код, ожидавший изменяемый список, падает в рантайме.
Подменяют через наследование от конкретного класса с переопределением половины методов. Композиция и интерфейс дают то же самое без хрупкой связи с родителем.
Мокают всё подряд, включая простые объекты-значения. Тест начинает проверять моки, а не поведение, и ломается на любом рефакторинге.