В своей провальной серии про UDF, вы поразительным образом рассуждаете про редьюсеры, чистые функции и вместе с этим лепите ООП с DI, похоронив сам Store под тонной ООП-шных абстракций. Вы уж либо трусы наденьте, либо крестик снимите. Какой swinject в UDF?! Контейнеры, фабрики координаторов, ассемблы... Хочется магически резолвить из контейнера - варите в MV*, че вас в функциональные архитектуры понесло...
ReduxJS плохой пример потому, что он используется в окружении где нет мощи типов или даже чего-то подобного на enum свифта. Если что, то и имена экшенов там тоже текстовые. Вот пример Point Free где ребята правильным образом подводят к тому как извлекать поведение для списка однородных сущностей. Без странных вещей типа primary-secondary car, firstDriver - secondDriver.
А Namespaceble костыль потому, что эта подкапотая инфраструктура начинает фигурировать в вызове из вьюшки:
Вы в ReduxJS где то видели в дисптаче явно переданный namespace? там такого нет и близко.
В иерархии экшенов та же проблема, вы рассматриваете сферического коня в вакууме и хардкодите элементы списов перечисляя их руками типа как здесь
enum AppActions: Action {
case primary(CarActions)
case secondary(CarActions)
// other actions
}
А когда вы пишите экстеншн на AppActions static func toPrimaryCarActions(action: AppActions) -> CarActions?
Это ж совсем дичь. В функциональной композируемой архитектуре вы либо ищите решения из этого мира, либо возвращайтесь обратно в привычный императивный мир MV* архитектур. И не говорите что потом всё переписали с CasePaths только потому, что в итоге выдернули кусок кода из TCA (который не сочетается с примерами редьюсеров, что вы писали выше) вообще не раскрыв тему pullback и извечения значений из enum. Советую пересмотреть PointFree и научиться делать сначала правильно, а потом изобретать велосипеды.
PS: То что вы не заставляете его использовать других - плохой аргумент, вы ж тут туториалы пишите. Человек который не понимаем причин и следствий потащит эту дичь в свой проект.
У вас проблема с инкапсуляцией поведения для состояний отдельных Car. Отсюда и костыль в виде протокола Namespacable. Раз уж вы знакомы с чуваками из Point Free и их библиотеками посмотрите блок видео о Composable Architecture по теме Derived Behavior. И уберите этот костыль.
Вы очень буквально восприняли мои примеры. Не понимая как правильно развивать этот подход, он естественно вам кажется полумерой и вы не видите что с ним делать дальшей. Не удивительно, почему вам не кажутся костылями протоколы, рендеры и контексты из тех примеров, которые вы сочли "отличным" решением. Лично я не вижу профита в том, что теперь у вас десяток новых сущностей. PostUpdateActionBus ставит точку в дальшейм обсуждении где тут костыли. В масштабе большого приложения - вы получите на порядок больше бойлерплейта, который невозможно автоматизировать. Ваш подход не плох, но и не настолько хорош, чтобы клеймить перечисления и людей которые умеют их использовать. За сим, прошу меня извинить.
Про алгебраические типы, типы-суммы и типы-значения - тема глубокая с далекоидущими выводами, поверхностный разбор ничего не даст. Надо углубляться.
Действительно, Swift оставил enum немного "ущербными" из-за чего можно сделать выводы наподобие ваших. Есть пара элементарных трюков которые возвращают в enum недостающей эргономики. Вот микропример, что имею в в иду.
struct Post: Equatable {}
struct Clip: Equatable {}
enum FeedItem {
case post(Post)
case clip(Clip)
// "секретный" ингредиент в виде небольшого шаблонного кода
var post: Post? {
guard case let .post(post) = self else { return nil }
return post
}
var clip: Clip? {
guard case let .clip(clip) = self else { return nil }
return clip
}
}
// профит
[<<FeedItem>>].compactMap(\.post) // -> [Post]
[<<FeedItem>>].compactMap(\.clip) // -> [Clip]
Там где вы описываете Equatable к enum Content - это излишне. достаточно чтобы ассорциированный значения реализовывали этот протокол, "ручная" реализация будет не нужна. Счетчики enum-ов и кастинга декрементируют ))
Вам не надо везде поддерживать FeedItem. Лучший кейс применения Enum с ассоциированными значениями - транспорт этих значений до конечного места использования без внедрения в сами значения их типовых различий. Вам так досадили перечесления потому, как мне показалось, что вы их использование пытаетесь привести к привычному императивному шаблону, подтыкая везде и всюду.
// ваш пример из статьи
extension FeedItem {
enum Content {
case post(Post)
case clips(Clips)
}
}
extension FeedItem.Content {
struct Post: Decodable {
static var feedType: String { return "post" }
let text: String
}
}
extension FeedItem.Content {
struct Clips: Decodable {
static var feedType: String { return "clips" }
struct Clip: Decodable {
}
let clips: [Clip]
}
}
В этой части где появляется FeedItem.Content.Post/Clip - ваша абстракция уже поплыла. Уберите Content и всё что вложено. Кажется в них нет никакой необходимости.
И все дальнейшие проблемы, снова решаем проблему тем же образом:
// Ваш вариант
enum FeedItemAction {
case notInterested(FeedItem)
case banPost(FeedItem.Content.Post)
}
struct FeedItemActionHandler {
func handle(_ action: FeedItemAction, items: [FeedItem]) -> [FeedItem] {
return items.compactMap { item in
switch action {
case .notInterested(let feedItem):
return feedItem == item ? nil : item
case .banPost(let post):
return item.content == .post(post) ? nil : item
}
}
}
}
// Как это могло бы выглядеть в жизни
enum FeedItemAction {
case notInterested(FeedItem)
case banPost(FeedItem) // FeedItem.Content.Post - главный костыль
// из-за которого всё шло наперекосяк
// и снова вычислимое свойство сильно упрощающее остальной код.
var feedItem: FeedItem? {
switch self {
case let .notInterested(item): return item
case let .banPost(item): return item
}
}
}
struct FeedItemActionHandler {
func handle(_ action: FeedItemAction, items: [FeedItem]) -> [FeedItem] {
return items.filter { $0 == action.feedItem }
}
}
Генерацию вычислимых свойств можно вынести Sourcery и не вспоминать об этих шаблонах и о том что надо "во всех местах" пойти и добавить новый case , думайте об этом также как о генерации Codable методов и прочей кодогенерации компилятором.
Держите enum простыми, без nested структур или других enum. Прячте паттерн матчинги в кодогенерацию (если религия позволяет, встречал массу ребят на дух не переносящих кодген). Это малая толика того что можно еще сделать для удобства. Надеюсь это поможет посмотреть иначе на enum-ы ))) Удачи!
Вы просто не разобрались в причинах появления enum как способа выражения возможных состояний. Ваш сфокусированный императиный подход просто не дает возможности раскрыть всю красоту и лаконичность enum. Чтобы понять этот инструмент и как его эргономично использовать, стоит вникнуть в алгебраические типы данных, функциональную композицию и научиться извлекать ассоциированные значения не привнося шаблоны паттерн матчинга непосредственно в бизнес логику. Для проникновения в глубины этого чудесного инструмента вам поможет сайт ребят из point free. Возможно вы о нем слышали. Успехов!
Вы молоды и еще много чего не видели ) давать советы неблагодарное дело, но я бы на вашем месте (а я был на вашем месте 12 лет назад) занимался углублением знаний, а не расширением. Широту взглядов легко спутать с очень узкой точкой зрения.
Вроде бы звучит уместно, но сбор метрик - это NFR (non functional requirements), а не продуктовая история, тем более, что упомянутые инструменты относятся сбору технических метрик, что там у вас крашнулось, упало, выбросило ошибку и тд. (Тут на хабре написано, в домклике 501 - 1000 сотрудников, но не нашлось профильных спецов?). Угадайте что будет, когда на следующей работе вас просят, чем вы занимались в домклик - красила кнопочки и поднимала кликхаус с графаной.
Абсолютно соглашусь с автором! Только кое-что хотелось бы дополнить: основное развитие в IT по моему мнению, состоялся именно в hardware части, которая и открыла возможности эффективного применения концепций программных технологий и методов, открытых в 50-70х годах (золотые годы информатики). Покуда железо не перейдет на радикально новый способ обработки радикального новой информации ( и тут прорыв сулят кубиты и квантовые технологии), до тех пор мы будем экстенсивно развивать уже придуманное.
Up to you! ) Однако расшибленная голова много пользы не принесёт ) Будьте внимательны и побольше учите основ. Например о том, что "@State" это не аннотация, а property wrapper. Вдруг еще решите ненароком, что "@discardableresult" и "@State" из одной оперы… Ну и структура это не класс… и еще куча ваших ляпов по незнанке… Сами не научитесь и других неправильно научите.
Опасные параллели ) это как с nodejs на rust переезжать, сначала кажется просто: вот те кнопка, вот те инпут. А потом оказывается что под капотом десятки фреймворков Apple с легаси на objective, ничего подобного на react router нет, строго типизированный язык руки отобьет до синевы, на проекте из 20 экранов хот релоад реакта прощально машет ручкой, а swiftui previews неожиданно начнут крашиться на лайв превью. Короче Лёня, рано бравируете молодой программистской удалью, реальность еще впереди
А я поддерживаю ) Наличие манифеста и следование ему не сделает тяп-ляпную эджайл разработку на 100% идеальной, но как отличный противовес менеджерскому разгулу — то, что нужно.
пощади отец, чего ж так дорого-то )
В своей провальной серии про UDF, вы поразительным образом рассуждаете про редьюсеры, чистые функции и вместе с этим лепите ООП с DI, похоронив сам Store под тонной ООП-шных абстракций. Вы уж либо трусы наденьте, либо крестик снимите. Какой swinject в UDF?! Контейнеры, фабрики координаторов, ассемблы... Хочется магически резолвить из контейнера - варите в MV*, че вас в функциональные архитектуры понесло...
ReduxJS плохой пример потому, что он используется в окружении где нет мощи типов или даже чего-то подобного на enum свифта. Если что, то и имена экшенов там тоже текстовые. Вот пример Point Free где ребята правильным образом подводят к тому как извлекать поведение для списка однородных сущностей. Без странных вещей типа primary-secondary car, firstDriver - secondDriver.
А Namespaceble костыль потому, что эта подкапотая инфраструктура начинает фигурировать в вызове из вьюшки:
store.dispatch(CarActions.breakDidPress("primary"))
Вы в ReduxJS где то видели в дисптаче явно переданный namespace? там такого нет и близко.
В иерархии экшенов та же проблема, вы рассматриваете сферического коня в вакууме и хардкодите элементы списов перечисляя их руками типа как здесь
А когда вы пишите экстеншн на AppActions
static func toPrimaryCarActions(action: AppActions) -> CarActions?
Это ж совсем дичь. В функциональной композируемой архитектуре вы либо ищите решения из этого мира, либо возвращайтесь обратно в привычный императивный мир MV* архитектур. И не говорите что потом всё переписали с CasePaths только потому, что в итоге выдернули кусок кода из TCA (который не сочетается с примерами редьюсеров, что вы писали выше) вообще не раскрыв тему pullback и извечения значений из enum. Советую пересмотреть PointFree и научиться делать сначала правильно, а потом изобретать велосипеды.
PS: То что вы не заставляете его использовать других - плохой аргумент, вы ж тут туториалы пишите. Человек который не понимаем причин и следствий потащит эту дичь в свой проект.
У вас проблема с инкапсуляцией поведения для состояний отдельных Car. Отсюда и костыль в виде протокола Namespacable. Раз уж вы знакомы с чуваками из Point Free и их библиотеками посмотрите блок видео о Composable Architecture по теме Derived Behavior. И уберите этот костыль.
Привет коллега! Абсолютно с тобой согласен, мой опыт сгенерировал аналогичную статью с анаогичными выводами только на vc )
Вы очень буквально восприняли мои примеры. Не понимая как правильно развивать этот подход, он естественно вам кажется полумерой и вы не видите что с ним делать дальшей. Не удивительно, почему вам не кажутся костылями протоколы, рендеры и контексты из тех примеров, которые вы сочли "отличным" решением. Лично я не вижу профита в том, что теперь у вас десяток новых сущностей.
PostUpdateActionBus
ставит точку в дальшейм обсуждении где тут костыли. В масштабе большого приложения - вы получите на порядок больше бойлерплейта, который невозможно автоматизировать. Ваш подход не плох, но и не настолько хорош, чтобы клеймить перечисления и людей которые умеют их использовать. За сим, прошу меня извинить.Про алгебраические типы, типы-суммы и типы-значения - тема глубокая с далекоидущими выводами, поверхностный разбор ничего не даст. Надо углубляться.
Действительно, Swift оставил enum немного "ущербными" из-за чего можно сделать выводы наподобие ваших. Есть пара элементарных трюков которые возвращают в enum недостающей эргономики.
Вот микропример, что имею в в иду.
Там где вы описываете Equatable к enum Content - это излишне. достаточно чтобы ассорциированный значения реализовывали этот протокол, "ручная" реализация будет не нужна. Счетчики enum-ов и кастинга декрементируют ))
Вам не надо везде поддерживать FeedItem. Лучший кейс применения Enum с ассоциированными значениями - транспорт этих значений до конечного места использования без внедрения в сами значения их типовых различий. Вам так досадили перечесления потому, как мне показалось, что вы их использование пытаетесь привести к привычному императивному шаблону, подтыкая везде и всюду.
В этой части где появляется
FeedItem.Content.Post/Clip
- ваша абстракция уже поплыла. Уберите Content и всё что вложено. Кажется в них нет никакой необходимости.И все дальнейшие проблемы, снова решаем проблему тем же образом:
Генерацию вычислимых свойств можно вынести
Sourcery
и не вспоминать об этих шаблонах и о том что надо "во всех местах" пойти и добавить новыйcase
, думайте об этом также как о генерации Codable методов и прочей кодогенерации компилятором.Держите enum простыми, без nested структур или других enum. Прячте паттерн матчинги в кодогенерацию (если религия позволяет, встречал массу ребят на дух не переносящих кодген). Это малая толика того что можно еще сделать для удобства. Надеюсь это поможет посмотреть иначе на enum-ы ))) Удачи!
Вы просто не разобрались в причинах появления enum как способа выражения возможных состояний. Ваш сфокусированный императиный подход просто не дает возможности раскрыть всю красоту и лаконичность enum. Чтобы понять этот инструмент и как его эргономично использовать, стоит вникнуть в алгебраические типы данных, функциональную композицию и научиться извлекать ассоциированные значения не привнося шаблоны паттерн матчинга непосредственно в бизнес логику. Для проникновения в глубины этого чудесного инструмента вам поможет сайт ребят из point free. Возможно вы о нем слышали. Успехов!
Вы молоды и еще много чего не видели ) давать советы неблагодарное дело, но я бы на вашем месте (а я был на вашем месте 12 лет назад) занимался углублением знаний, а не расширением. Широту взглядов легко спутать с очень узкой точкой зрения.
Вроде бы звучит уместно, но сбор метрик - это NFR (non functional requirements), а не продуктовая история, тем более, что упомянутые инструменты относятся сбору технических метрик, что там у вас крашнулось, упало, выбросило ошибку и тд. (Тут на хабре написано, в домклике 501 - 1000 сотрудников, но не нашлось профильных спецов?). Угадайте что будет, когда на следующей работе вас просят, чем вы занимались в домклик - красила кнопочки и поднимала кликхаус с графаной.
Зачем в домклике фронтендеров мучают кликхаусом и графаной? ?♂️
Сказал знаменитый Дуров.
Вы видимо не разобрались или не умеете его готовить. UDF архитектура - одна из самых слабосвязанных и легкотестируемых архитектур.
Ребята, а че вообще с вашим Спейсом? Уж очень долгий долгострой. Где новости?
Абсолютно соглашусь с автором! Только кое-что хотелось бы дополнить: основное развитие в IT по моему мнению, состоялся именно в hardware части, которая и открыла возможности эффективного применения концепций программных технологий и методов, открытых в 50-70х годах (золотые годы информатики). Покуда железо не перейдет на радикально новый способ обработки радикального новой информации ( и тут прорыв сулят кубиты и квантовые технологии), до тех пор мы будем экстенсивно развивать уже придуманное.