в моменте времени. дизайн оценивается при изменениях кода - если легко менять и поддерживать - значит дизайн хорош.
во вторых - надо заглянуть чуть дальше чем 6 или 7 кодов - пусть их будет 30, а лучше 300 - в таком случае иметь огромный свич уже больно. да даже на 30 пунктов - уже начинается мигрень. сейчас конечно их мало, но время идет и количество вырастет полюбому.
с чего начинается просто решение - when с кучей веток, или даже, правильнее сказать - с кучей блоков кода.
самый удобный кейс - однообразные действия, типа, перевести код ошибки и показать юзеру. но обычно этого мало, т.к. ошибка несет кроме информативного еще и другой смысл - надо что то сделать с текущим контекстом - отменить транзакцию, подсказать пользователю что делать, выделить ошибку на экране, мало ли что еще.
получается в не-самом-удобном кейсе каждый блок в структуре when будет иметь какую то свою функциональность. что получается:
1. метод в котором находится when разрастается
2. каждый блок кода обрастает своими зависимостями
3. как следствие - класс требует больше зависимостей
пусть например количество кейсов выросло до 30 - и LOC метода выросло до 1000
следующий этап рефакторинга - а давайте разнесем блоки кодов по отдельным методам класса - в целом идея неплохая и рабочая
но опять же - количество методов класса будет N (кодов) + 1 (when), и будет расти параллельно с новыми кодами ошибок
помимо прочего - растет и конструктор класса в который надо запихивать все больше зависимостей
взглянем на секунду в разрезе тестирования - чтобы протестировать практически голую стейт машину требуется сколько то там мокнутых зависимостей
идем дальше с рефакторингом - предположим что разнесем методы класса по отдельным классам-хендлерам и будем их создавать в init основного класса - это почти то же самое только вид сбоку. каждый раз придется добавляя класс - добавлять вызов его конструктора в инит, и не забывать добавлять зависимости
можно подумать и придумать - а давайте просто будем добавлять эти конкретные классы хендлеров ошибок в конструктор основного класса, типа как
MyErrorHandler constructor( val Ahandler, val Bhandler, val Chandler)
отлично - вот только количество аргументов будет еще больше чем просто с
MyErrorHandler constructor( val Adep, val Bdep, val Cdep){
val a = Ahandler(Adep)
val b = Bhandler(Bdep)
val c = Chandler(Adep, Cdep)
...
}
все еще слишком много аргументов конструктора
идем еще дальше и рефакторим как список хендлеров с единым интерфейсом
MyErrorHandler constructor (val handlers: List<iHandler>)
fun onErrorCode(code){
for (handler in handlers){
if(handler.accept(code){ return }
}
throw Error("no handler for code $code")
}
и где то в di модуле этот список наполняется отдельными хендлерами
ретро - тестируется? да тестируется. надо ли тестировать каждую ветку в when? нет, т.к. это тесты каждого отдельного класса iHandler
расширяется? да. стало ли меньше писанины по сравнению с when - нет не стало - точно также делается отдельный класс и где то его надо регистрировать
боли станет меньше с редактированием такого хендлера, т.к. редактировать по сути придется только список зависимостей, который из конструктора класса переезжает в di
в чем минусы: when проверяет исключительно все случаи - если только проверяемый код это Enum или силед - но у нас int изначально и если не усложнять его - тогда вариант с хендлерами подходит
алсо, если все же стоим на стороне енумов или силедов - отлично, вот только на каждую новую ошибку придется делать два класса - енум и его хендлер, так что кмк проще оставлять инты
ну и конечно я бы тоже when сделал для небольшого количества кейсов. но как только почувствуется какое то нехорошее жжение в одном месте - сразу же надо рефакторить
Подход с листом хэндлеров понятный, только не совсем понятно как соотносить хэндлер с кодом статуса, в методе accept у конкретного хэндлера делать проверку на то, равен ли приходящий статус задекларированному самим хэндлером?
это необязательно так у ошибок могут быть эмпирические правила например "код ошибки меньше 500 делаем это" "код выше 1000 делаем что то другое"
Обсуждают сегодня