у меня складывается ощущение из такого описания trait, что это аналог протокола или интерфейса, то есть как раз реализация того дизайна, что я пропагандирую
А вот про Result Ok/Error мне ближе описанный в части про сигналы подход с разделением обработки onOk onError. И мой аргумент тут такой: генератор результат уже выполнил ветвление, он выбрал, в какую из ветвей направить исполнение, почему после этого все слушатели (пользователи API) должны выполнять снова это ветвление, если они могут просто зарегистрировать обработчики на ветвление источника?
Но, конечно, такой код возможен только там, где есть возможность зарегистрировать и вызвать конкретный обработчик. При получении ответа из сети или при десериализации хранящихся на диске данных, когда у нас есть только бинарные данные, которые могут либо OK либо Error содержать — в таком случае от ветвления на стороне получателя уже не избавиться, кажется. Но этот случай в статье я рассматриваю как допустимый для какой-то реализации перебора
возможно:) я допускаю enum как допустимый (не лучший или самый правильный, но допустимый) инструмент для быстрого описания набора вариантов. но если же, как я написал в заключении, это множество вариантов начнёт развиваться — очень стоит задуматься над выделением абстракции и неизменяемого общего поведения хотя бы для безопасности кода общего поведения и простоты понимания отдельных сценариев
про бесполезность enum — я считаю этот инстурмент скорее ненужным и бесполезным, чем полезным. поэтому в конце статьи и написал, что по умолчанию против него :) во всяком случае, теперь сам стараюсь не использовать этот инструмент никогда.
Статья не про язык, а про организацию кода без переборов и переплетения сценариев.
Не буду скрывать, действительно rust не изучал и не имел с ним дела. Мне стоит восполнить этот пробел. Я иногда слышу положительные отзывы о его использовании
Когда у вас изменяется enum, то вы сразу видите, в каких местах нужно поправить код, работающий с enum.
Этот аргумент я слышал довольно часто, но у меня есть ответ, что в случае с выбором протокола IDE тоже будет ругаться на отстувие определния какого-то метода в какой-то из реализаций протокола
Про множество абстракций — в общем случае я предлагаю лишь одну под один enum: протокол, который описывает, что мы хотим в общем от каждого элемента
я описываю в статье не отказ от switch в пользу перехода на какой-то другой менее эффективный способ ветвления. я описываю дизайн кода, который вообще избегает лишних ветвлений и проверок в пользу вызова интерфейсных методов. так же в обработке событий ещё и приведён пример избавления от необходимости перебирать весь массив элементов для поиска тех, что затронуты возникшим событием. Так что, возможно, описанное даже эффективнее по времени исполнения, чем множество последовательных супер оптимизированных switch. Но тут не берусь утверждать.
ну именно с лентой Вконтакте в iOS приложении реализация описанного механизма с протоколами и контекстом элементов помогла начать писать изолированный код новых элементов ленты, при этом даже в разных модулях, и на Swift, хотя основной код старый код ленты остаётся на objc и его логика не меняется. Так что в основном модуле клиента (в котором исторически находится реализация основной логики ленты) даже нет смешения языков ("Mix and Match" Swift и Objc)
ну и если не вынести состояния в сущности, то тут получается, что система описывает как раз не разные состояния бизнес-процесса, то есть не бизнес-процесс по возможным этапам-шагам и явными переходами между шагами, а переборы всех возможных состояний с описанными в статье проблемами поддержки такой системы.
если для какого-то проекта проще/безопаснее/нагляднее явно перебирать состояния как значения — тогда, возможно, не стоит ничего трогать. но при росте системы стоит всё же учитывать альтернативные варианты реализации
если эти состояния системы не просто флаги, а режимы работы системы, логика которых хоть чем-то различается, вомзожно, имеет смысл в рамках слоя, для которого это важно, реализовывать состояние как сущность, ведь, по сути, состояние является сценарием бизнес логики.
если при этом есть необходимость передавать информацию о состоянии наружу — тут два варианта, которые я тоже рассмотрел в части статьи про сигналы:
1) можно сделать интерфейс с передачей констант (1=>Заказ и т д)
2) можно сделать интерфейс с регистрацией обработчиков на событие перехода в каждое отдельное состояние, и в обработчики передавать строго типизированные данные по конкретному состоянию.
И вот про выбор между этими двумя вариантыми как раз мой открытй вопрос про реактивные библиотеки
но, кажется, std::variant является более честным и гибким инструментом, так как принадлежность какому-то набору вариантов не является частью объявления класса и почти что его namespace, а определяется по месту реального использования.
всё хорошо, если я правильно понял, вы подсветили другую проблему, которая тоже встречается в коде: выделение разных значений одной сущности (одного сценария) в разные сущности.
в статье приведены примеры, когда одним словарём под один `enum` не обойдёшься. Здесь специально для примера взяты свифтовые перечисления с ассоциированными значениями. При этом у разных case разнотипные значения. У Post это Text, у блока клипов — это массив клипов. То есть, по сути, это абсолютно разные сущности, жёстко собранные в один enum. Эти сущности объединяет только то, что они должны быть показаны в ленте (а в ведь в реальном проекте они могут потребоваться не только в ленте). Если бы в качестве enum в таком примере был бы использован только FeedType с перечислением строковых констант типов ленты, и перебор значений был только в парсинге — это было бы лучше, и такой сценарий с перебором типов элементов гетерогенного списка, полученного из другого слоя, я в статье описываю, как допустимый. Но и тут может хватить просто констант, даже не обязательно жёстко зафиксированных одним списком, особенно если есть желание или необходимость не перекомпилировать модуль ленты при добавлении новых типов элементов ленты, если при этом сама логика ленты не меняется.
для экспортирования API, каких-то констант, вполне возможно. но если речь о конкретной системе, в которой это не просто константы, а реальные состояния системы, возможно, стоит задуматься о введении сущностей под эти состояния. надо смотреть каждый конкретный случай
конечно же в статье речь об использовании инструмента, а не самом инструменте. но лично для меня в целом любое появление перечислений в коде — повод задуматься, уместно ли оно там
безусловно. но тут в статье другое противопоставление: не enum против каких-то конкретных экземпляров (значений свойств объекта из хранилища); а enum против сущности (объекта, класса, структуры), на которую накладывается контракт для поддержки работы в каком-то общем механизме (в статье это новостная лента)
1) Моё мнение такое, что нужно использовать связку UILabel/UITextView и методов boundingRect с предосторожностью, так как я нигде в документации не нашёл подтверждения, что в основе layout NSString/NSAttributedString лежит используемый UILabel/UITextView TextKit. То есть, тот факт, что boundingRect в каких-то случаях (в случае UILabel) совпадает с результатами работы TextKit — недокументированное поведение. Но если будет подтверждение, что в основе лежат абсолютно одни и те же методы, и в эти методы передаются абсолютно идентичные аргументы — конечно, можно использовать boundingRect + UILabel. Для примера код playground. Здесь можно заметить, что результаты UILabel.sizeThatFits и NSAttributedString совпадают, если округлить в бОльшую сторону результат boundingRect с учётом screen scale, то есть до ближайшего бОльшего количества пикселей. Для UITextView значения уже не совпадают, хотя согласно документации TextKit в UITextView тоже используется TextKit, но, явно, с другими параметрами (причём, этими параметрами мы сами можем управлять). Конечно, можно и для UITextView попробовать подобрать параметры boundingRect, но насколько это корректно и детерминировано?
2) Мы сейчас в Ленте не используем boundingRect но из прошлого опыта смутно помню, что раньше приходилось подбирать параметры и константы отступов, чтобы результат boundingRect совпадал с тем, как UILabel располагает текст внутри себя.
у меня складывается ощущение из такого описания trait, что это аналог протокола или интерфейса, то есть как раз реализация того дизайна, что я пропагандирую
А вот про Result Ok/Error мне ближе описанный в части про сигналы подход с разделением обработки onOk onError. И мой аргумент тут такой: генератор результат уже выполнил ветвление, он выбрал, в какую из ветвей направить исполнение, почему после этого все слушатели (пользователи API) должны выполнять снова это ветвление, если они могут просто зарегистрировать обработчики на ветвление источника?
Но, конечно, такой код возможен только там, где есть возможность зарегистрировать и вызвать конкретный обработчик. При получении ответа из сети или при десериализации хранящихся на диске данных, когда у нас есть только бинарные данные, которые могут либо OK либо Error содержать — в таком случае от ветвления на стороне получателя уже не избавиться, кажется. Но этот случай в статье я рассматриваю как допустимый для какой-то реализации перебора
возможно:) я допускаю enum как допустимый (не лучший или самый правильный, но допустимый) инструмент для быстрого описания набора вариантов. но если же, как я написал в заключении, это множество вариантов начнёт развиваться — очень стоит задуматься над выделением абстракции и неизменяемого общего поведения хотя бы для безопасности кода общего поведения и простоты понимания отдельных сценариев
к теме статьи не относится, но:
мы следим за производительностью, постоянно стараемся её улучшить. к сожалению, не все проблемы пока удалось исправить.
если это не разовая проблема, а стабильное воспроизведение — будем очень признательны, если сможете передать детали проблемы нашей техподдержке
про бесполезность enum — я считаю этот инстурмент скорее ненужным и бесполезным, чем полезным. поэтому в конце статьи и написал, что по умолчанию против него :) во всяком случае, теперь сам стараюсь не использовать этот инструмент никогда.
Статья не про язык, а про организацию кода без переборов и переплетения сценариев.
Не буду скрывать, действительно rust не изучал и не имел с ним дела. Мне стоит восполнить этот пробел. Я иногда слышу положительные отзывы о его использовании
Этот аргумент я слышал довольно часто, но у меня есть ответ, что в случае с выбором протокола IDE тоже будет ругаться на отстувие определния какого-то метода в какой-то из реализаций протокола
Про множество абстракций — в общем случае я предлагаю лишь одну под один enum: протокол, который описывает, что мы хотим в общем от каждого элемента
я описываю в статье не отказ от switch в пользу перехода на какой-то другой менее эффективный способ ветвления. я описываю дизайн кода, который вообще избегает лишних ветвлений и проверок в пользу вызова интерфейсных методов. так же в обработке событий ещё и приведён пример избавления от необходимости перебирать весь массив элементов для поиска тех, что затронуты возникшим событием. Так что, возможно, описанное даже эффективнее по времени исполнения, чем множество последовательных супер оптимизированных switch. Но тут не берусь утверждать.
ну именно с лентой Вконтакте в iOS приложении реализация описанного механизма с протоколами и контекстом элементов помогла начать писать изолированный код новых элементов ленты, при этом даже в разных модулях, и на Swift, хотя основной код старый код ленты остаётся на objc и его логика не меняется. Так что в основном модуле клиента (в котором исторически находится реализация основной логики ленты) даже нет смешения языков ("Mix and Match" Swift и Objc)
"Любое появление любых конструкций языка в коде - это всегда повод задуматься, уместно ли оно там." — согласен
статья, на мой взгляд, даже о более общей проблеме, частным и частым примером которой является именно
enum
.ну и если не вынести состояния в сущности, то тут получается, что система описывает как раз не разные состояния бизнес-процесса, то есть не бизнес-процесс по возможным этапам-шагам и явными переходами между шагами, а переборы всех возможных состояний с описанными в статье проблемами поддержки такой системы.
если для какого-то проекта проще/безопаснее/нагляднее явно перебирать состояния как значения — тогда, возможно, не стоит ничего трогать. но при росте системы стоит всё же учитывать альтернативные варианты реализации
я скорее вот про что:
если эти состояния системы не просто флаги, а режимы работы системы, логика которых хоть чем-то различается, вомзожно, имеет смысл в рамках слоя, для которого это важно, реализовывать состояние как сущность, ведь, по сути, состояние является сценарием бизнес логики.
если при этом есть необходимость передавать информацию о состоянии наружу — тут два варианта, которые я тоже рассмотрел в части статьи про сигналы:
1) можно сделать интерфейс с передачей констант (1=>Заказ и т д)
2) можно сделать интерфейс с регистрацией обработчиков на событие перехода в каждое отдельное состояние, и в обработчики передавать строго типизированные данные по конкретному состоянию.
И вот про выбор между этими двумя вариантыми как раз мой открытй вопрос про реактивные библиотеки
но, кажется, std::variant является более честным и гибким инструментом, так как принадлежность какому-то набору вариантов не является частью объявления класса и почти что его namespace, а определяется по месту реального использования.
про std::variant из C++ — да, кажется, это похоже. Мне на ум из аналогий ещё приходит конструкция Sealed classes
всё хорошо, если я правильно понял, вы подсветили другую проблему, которая тоже встречается в коде: выделение разных значений одной сущности (одного сценария) в разные сущности.
в статье приведены примеры, когда одним словарём под один `enum` не обойдёшься. Здесь специально для примера взяты свифтовые перечисления с ассоциированными значениями. При этом у разных case разнотипные значения. У Post это Text, у блока клипов — это массив клипов. То есть, по сути, это абсолютно разные сущности, жёстко собранные в один enum. Эти сущности объединяет только то, что они должны быть показаны в ленте (а в ведь в реальном проекте они могут потребоваться не только в ленте). Если бы в качестве enum в таком примере был бы использован только FeedType с перечислением строковых констант типов ленты, и перебор значений был только в парсинге — это было бы лучше, и такой сценарий с перебором типов элементов гетерогенного списка, полученного из другого слоя, я в статье описываю, как допустимый. Но и тут может хватить просто констант, даже не обязательно жёстко зафиксированных одним списком, особенно если есть желание или необходимость не перекомпилировать модуль ленты при добавлении новых типов элементов ленты, если при этом сама логика ленты не меняется.
для экспортирования API, каких-то констант, вполне возможно. но если речь о конкретной системе, в которой это не просто константы, а реальные состояния системы, возможно, стоит задуматься о введении сущностей под эти состояния. надо смотреть каждый конкретный случай
конечно же в статье речь об использовании инструмента, а не самом инструменте. но лично для меня в целом любое появление перечислений в коде — повод задуматься, уместно ли оно там
безусловно. но тут в статье другое противопоставление: не enum против каких-то конкретных экземпляров (значений свойств объекта из хранилища); а enum против сущности (объекта, класса, структуры), на которую накладывается контракт для поддержки работы в каком-то общем механизме (в статье это новостная лента)
Спасибо, будем стараться!
1) Моё мнение такое, что нужно использовать связку
UILabel
/UITextView
и методовboundingRect
с предосторожностью, так как я нигде в документации не нашёл подтверждения, что в основе layoutNSString
/NSAttributedString
лежит используемыйUILabel
/UITextView
TextKit. То есть, тот факт, чтоboundingRect
в каких-то случаях (в случаеUILabel
) совпадает с результатами работы TextKit — недокументированное поведение. Но если будет подтверждение, что в основе лежат абсолютно одни и те же методы, и в эти методы передаются абсолютно идентичные аргументы — конечно, можно использоватьboundingRect
+UILabel
. Для примера код playground. Здесь можно заметить, что результатыUILabel.sizeThatFits
иNSAttributedString
совпадают, если округлить в бОльшую сторону результатboundingRect
с учётом screen scale, то есть до ближайшего бОльшего количества пикселей. ДляUITextView
значения уже не совпадают, хотя согласно документации TextKit вUITextView
тоже используется TextKit, но, явно, с другими параметрами (причём, этими параметрами мы сами можем управлять). Конечно, можно и дляUITextView
попробовать подобрать параметрыboundingRect
, но насколько это корректно и детерминировано?2) Мы сейчас в Ленте не используем
boundingRect
но из прошлого опыта смутно помню, что раньше приходилось подбирать параметры и константы отступов, чтобы результатboundingRect
совпадал с тем, какUILabel
располагает текст внутри себя.