Как избавиться от switch и реализовать обработку разных типов документов через интерфейсы и Spring бины
Что проверяют
Умеете ли вы заменить ветвление по типу полиморфизмом и при этом опереться на то, как Spring собирает бины, а не на ручную фабрику. Следом почти всегда спрашивают: «что нужно сделать, чтобы добавить новый тип документа?» Правильный ответ: написать один новый класс и больше ничего не трогать.
Как отвечать
Каркас из трёх шагов:
- интерфейс-стратегия:
DocumentHandlerс методамиDocumentType type()иvoid handle(Document doc). Тип лучше держать вenum, а не в строке, тогда опечатку поймает компилятор; - реализации: по классу на тип, каждый помечен
@Component. Логика конкретного формата живёт только в нём; - реестр: сервис получает все реализации через конструктор как
List<DocumentHandler>и один раз раскладывает их вEnumMap<DocumentType, DocumentHandler>. Если два обработчика заявили один тип, приложение должно упасть на старте, а не в рантайме.
Вызов после этого выглядит как handlers.get(doc.type()).handle(doc) плюс внятное исключение, если обработчика нет. Spring умеет инжектить и Map<String, DocumentHandler>, где ключ это имя бина, но привязывать логику к имени бина хрупко: переименовали класс, и выбор сломался.
Назовите принципы: это паттерн «Стратегия» и принцип открытости/закрытости, новый формат добавляется без правки существующего кода. Упомяните тесты: реестр проверяется обычным юнит-тестом без поднятия контекста, достаточно передать в конструктор список заглушек.
Если на один тип приходится несколько обработчиков, пригодятся @Order и метод boolean supports(Document doc) с перебором по порядку, это уже «цепочка обязанностей».
Где спотыкаются
Достают бин через ApplicationContext.getBean(type + "Processor"). Это service locator: зависимость спрятана, ошибка в имени всплывает только при вызове, а имя бина по умолчанию обычно начинается со строчной буквы, и склейка строк с ним не совпадает.
Переносят switch в фабрику и считают задачу решённой. Ветвление никуда не делось, оно просто переехало в другой класс.
Забывают про случай «обработчика нет» и про дубли. map.get() возвращает null, и падение случается далеко от причины.