Комментарии 21
Решение не самое красивое, однако сохраняет читаемость и можно быть уверенным, что ветка default никогда не будет исполнена.
Погрепайте свои продакшен логи на предмет наличия в них исключений которые никогда не произойдут. Узнаете много интересного.
На 17 JDK оно тоже нормально пишется без ветки по умолчанию. Скастите в энум, например. Тоже упадет с исключением, но хотя бы в более понятном месте и с более понятной ошибкой.
У нас в код со сравнением имен классов не под сильной нагрузкой, не нашел ни одного лога с выполненной веткой default. Я честно не могу представить ситуацию, в которой она выполнится - поделитесь пожалуйста, если встречали такое.
С enum'ами вариант неплохой, но упрощая switch усложнится весь остальной код, т.к. из сервиса мы хотим получить уже готовую модель для сериалиизации.
У тебя и в сервисе ифчик(тернарный оператор) и в контролёре тот же ифчик(свич).
В сервисе типа: if(result==null) return "error", а потом в контроллера if ("error") Return errorResponce;
Ну добавь ещё класс напиши там if(errorResponce) return 0; полимофизмом только ифчик сделай, чтоб по солиду. Ещё три класса будет, ништяк же.
Логика божественная: чот контроллер пустой, давай ка накрутим какой нить фигни.
Ну и ещё я мож пьян, но это ваще норм возвращать не один класс, а чо попало? Ну типа захотели sessionInfo вернули, захотли additonalInfo, там номеров ошибок дочерта, давайте на каждую свою фиготу возвращать? Ломбок справится с описанием этой дичи кстати?
Не Ломбок, а swagger
В сервисе тернарный оператор поставил для простоты (в статье приписка, что детали пропущены), в реальном коде все может быть сложнее.
Идея как раз в том, чтобы возвращать не что попало, а ограниченный набор классов - такую гарантию дает компилятор. Буду рад посмотреть твой вариант обработки описанного кейса.
Swagger генерит то, что в аннотациях указано, поэтому думаю справится. Не могу сказать точно, т.к. мы не генерим в том конкретном проекте.
Представьте приходит программист на ваш проект, ему надо срочно описание ошибк починить он c ctrl-shift-f @controllerAdviii ищет нужный перехватчик и правит, а не разбирается в вашем велике с 21 жавой.
Вы пишете учебный пример, он должен быть идеален. А у большинства мысль, ну это @controllerAdvice прямее сделать можно, а ту наговнокожено ради 21 жавы только.
Сваггер скорей всего поломали, апи спроектировано криво, стандартные паттерны выкинули.
Вы ваще не поняли, нахрена это. Я думаю, это для библиотеке сугубо чтоб интерфейсы переопределить не пытались. В вашем случае переопределить интерфейс проще простого.
Зы. Мне пофиг как вы напишете if else свичом, тернарником, полиморфизмом или будете в цикле перебирать, пока не совпадет, прикол в том что вы сделали один ифчик дважды.
В сваггере есть @ApiResponse, там можно определить тело для конкретного ответа.
Автор, вроде, указал, почему не хочет выносить оное в controllerAdvice. Как я понял, этот ответ является бизнесовой логикой конкретной конечной точки, и пихать оное в обработчик дефолтных ошибок не очень, потому что сам ответ не универсален.
Да и как бы то ни было, sealed само по себе решение спорное и на любителя, тут, скорее всего, не будет никаких "идеальных" примеров даже учебных. Вот вы пишете, что это можно использовать в либах, что б нельзя было переопределять интерфейс, но это значит, что пользователю в принципе не надо знать об этом интерфейсе. А почему тогда он открытый? А куда девается весь наш полиморфизм? Какой смысл в интерфейсе, реализацию которого мы не можем переопределить?
Сваггер скорей всего поломали, апи спроектировано криво, стандартные паттерны выкинули
После такого токсичного потока предположений относительно совершенно незнакомого проекта и людей не хочется отвечать, но я все-таки отвечу.
Во-первых, как я уже отвечал ниже и как написано в статье, подход с sealed классами не предполагает замену @ControllerAdvise - обрабатывать исключения все еще лучше в нем. Описанный в статье подход стоит использовать, когда возможны два и более валидных юзер флоу, т.е. это не ошибка, просто вариантов куда отправить юзера дальше больше чем один. В статье я описал другие варианты решения подобного кейса и также описал, чем они мне не нравятся.
Во-вторых, когда разработчик приходит, то он обычно имеет время разобраться с код-стайлом и подходами, которые использует команда - онбординг, в общем. У нас он есть, также как и код-ревью, на первые задачи новоприбывшего команда уделяет особое внимание. Если у вас разработчик должен с места в карьер прыгать - что ж, могу лишь посочувствовать. Ну а если разработчик игнорирует "велик с 21 жавой", решение даже первой задачи у него просто поиск по через cmd-shif-f без просмотра хотя бы входной точки (контроллера) - то я бы не хотел с таким разработчиком работать.
Наконец, я не понимаю, почему тебя так тригернул тернарный оператор + switch из примера, они в разных классах. Может, ты еще приходишь в ярость, когда метод возвращает Optional? Там ведь тоже (о ужас) при создании Optional может быть if, и при его обработке тоже.
Объясняю, представь что на входе в туалет две бабушки. Первая выдает розовый билетик девочкам, синий билетик мальчикам.
А во второй отдельной комнате, сидит вторая бабушка, которая направляет тех у кого розовый билетик в женский туалет, а тех у кого синий в мужской туалет.
Те не кажется, что схема усложнена? И можно оставить одну бабушку, которая в зависимости от пола направляет человека в нужный туалет?)
Так вот первая бабушка тернарник, вторая свич. И если у тебя появится транссексуалы, те придется выжать бабушке ещё один жёлтый билетик и второй бабушке дать инструкции чо делать. А потом инвалида, собачки и понеслось.
Аналогия хорошая, но в вакууме. Можешь, пожалуйста, предоставить код, который решает кейс из статьи без "двойного if"? У тебя скорее всего будет код, который из сервиса вернет ResponseEntity, имхо это плохая практика.
Это наистандартнейшая задача.
Пользователь вводит логин пароль, чему-либо возвращается сессия, либо сообщение, что логин-пароль неправильный, просрочен, уже есть другой вход,что угодно.
Ты всерьез, спрашиваешь меня как это сделать?
Через контролерэдвайс и не трахать мозг.
Добавить в responce объект errors и наполнять его.
Разрешите встряну. Зачастую бывает, что между первой и второй бабушками изначальный мальчик может стать девочкой(осуждаем) и выполнить определенную бизнес-логику. Второй бабушке неизвестно, что девочка была мальчиком, но зато есть синий билетик.
Про два валидных юзер флоу в рест запросе - это сесть и плакать. Ты про круд слышал чо нить?)
Ну типа послали тебе ПОСТ создать сессию а ты либо сессию вернул, либо юзера, либо ошибку, либо фотку голой Пенелопы Круз.
Какать в коде оправдываясь тем, что есть онбординг, это прям выкидывать за Шкирняк сразу. А если в коде миллионы строк, и на каждом шагу ваша гениальность ушки показывает?
Мне вот интересно, общаться в уничижительной манере обязательно? Вроде культурные люди.
Добавляю контекста: POST возвращает либо сессию, либо требование пройти двухфакторку, там сложная бизнес-логика которая это делает. Оба варианта прописаны в API спецификации, контракт прозрачный. Как (и главное зачем) это API переделывать?
За "шкирняк" надо выкидывать людей, которые не разобравшись обзывают чужие решения говнокодом. Если бы все на свете можно было сделать через if, не теряя читаемости и расширяемости, то все бы так и делали. Но это не так, и например кейс из статьи был вереницей тройных вложенных if'ов в куче классов, с перемешенной логикой и возвратом из контроллера и сервиса ResponseEntity<Object>. Зато без "великов ради джавы 21".
Напоследок добавлю еще раз, что я не призываю срочно везде повтыкать switch'ей с обвязкой из интерфейсов. Я предложил подход для случаев, когда выносить в ControllerAdvice не подходит, а возвращать из сервиса сформированный ответ не позволяет разделение ответственности между контроллером и сервисом. Не нравится или не подходит под твой кейс - не используй, зачем оскорблять автора?)
Мне недавно sealed классы зашли с джексоном и его JavaTypeName. Правда вручную пришлось зарегать наследников при построении маппера, но это выглядело лучше, чем дублирование кода в permits и аннотациях. Есть открытый issue, в котором даже посоветовали готовый сторонний модуль, который автоматизирует регистрацию сабтипов. Под рукой нет ссылок, извините.
В более ранних версиях java можно применить visitor. Несколько громоздко, но нет проблемы с default и с «я забыл добавить обработку нового возвращаемого класса»
Дальше у вас апи вырастает до 500 эндпоинтов. В каждом будете писать switch и придумывать набор силд-классов по все возможные исходы? У вас очень быстро появится желание навести порядок в этом зоопарке и не повторяться. И выбор будет невелик. Или придумаете монадоподобный контейнер с обработчиком или вернетесь к идее каталога исключений и ControlerAdvice. В общем-то оба подхода про одно и то же: делегировать разбор ошибок куда-то под коврик, чтобы под ногами не путался. "Размазывание бизнес-логики" здесь - скорее проблема какой-то конкретной архитектуры, а не проблема этого паттерна в целом.
Я не думаю, что каждый эндпоинт будет нуждаться в таком. В API, из которого я взял пример, достаточно много эндпоинтов, и в большинстве случаев все решается через ControllerAdvice (в статье например так решен вариант с 500). Однако бывают случаи, когда предполагается два валидных ответа юзеру, по сути 422 в примере это не ошибка, а нормальный юзер флоу. В таких случаях, на мой взгляд, возврат sealed интерфейса подойдет.
Можете пожалуйста раскрыть идею с "монадоподбным контейнером с обработчиком"?
Тут наверное можно поиметь проблемы со spring, если попробовать заижектить объект в поле или параметр у которого тип это sealed интерфейс. Оно может при исполнении рвануть и не сразу будет понятно от чего.
Применяем Java Sealed Classes на практике