Тема, конечно, не нова, и немало на этот счет уже сказано и написано. Но все же поделюсь и своим взглядом на этот счет, т.к. мое осознание данного факта формировалось скорее независимо и на основании собственного опыта, ценностей и взглядов, и возможно (надеюсь) в нем читатель найдет для себя что-то свежее или полезное.
Flux - это вовсе не что-то новое либо революционное
Не то, чтобы я не люблю его за это. Скорее, в этом даже нет ничего плохого - если решение хорошее и проверенное временем, разве это плохо? Скорее мне просто удивительно слышать, как подход, реализованный в Flux в целом (и в Redux в частности) некоторые пытаются выдавать за что-то инновационное и революционное. Да и само решение, на мой взгляд, как минимум не лишено недостатков. Но об этом далее, а пока вспомню молодость.
В начале нулевых я разрабатывал ПО и библиотеки компонент на Delphi под Windows (сначала Win9x, потом XP). В операционных системах Windows с самых первых, если не ошибаюсь, версий, для визуальных элементов интерфейса (кнопки, поля ввода) существует понятие окна - да, окно это не только то, что с рамкой, почти любой визуальный элемент управления имел свое собственное окно. Окно в данном случае - это некая структура в памяти, которая имеет ассоциированный с ним идентификатор (window handle) и оконную функцию (см. далее). Если мы хотим выполнить какое-либо действие над элементом, например - изменить текст кнопки, мы должны упаковать это действие в специальную структуру-сообщение (Window message) и отправить ее соответствующему окну. Структура состоит из закодированного типа сообщения (например WM_SETTEXT - для установки текста) и собственно payload. Будучи отправленным, сообщение не попадает в обработчик напрямую - вместо этого оно отправится в очередь, из которой его извлекает некий диспетчер и вызывает оконную функцию того окна, в которое мы сообщение отправили, передав его в виде параметра. Оконная функция в простейшем случае - это большой switch, где в зависимости от типа сообщения мы передаем управление более конкретному обработчику. Ничего не напоминает?
Те времена давно прошли и больших сожалений на этот счет нет. Более широкое использование ООП со временем значительно улучшило качество кода по сравнению с чистым WinAPI с его сообщениями и оконными функциями. И сегодня, наблюдая код с использованием Redux, определенное чувство дежавю возникает.
Нарушение принципа "Low coupling, high cohesion"
Если вы ищите простую и понятную формулировку, что такое качественный дизайн, то эти четыре слова из подзаголовка коротко и емко его описывают - внутри модуля или компонента его элементы должны быть тесно связанны друг с другом, в то время как связи между отдельными модулями/компонентами должны быть слабыми. Это базовая ценность. Все остальные принципы и подходы в проектировании - следствия из этого принципа. "Low coupling, high cohesion" отвечает на вопрос "чего мы хотим добиться", в то время как, скажем, SOLID-принципы или любой из Design Pattern указывает нам "как мы можем этого добиться".
И вот тут Redux подводит - то, что должно быть цельным внутри компонента, оказывается размазанным по множеству файлов и сущностей - получаем Low cohesion вместо High. Связи, которые должны оставаться внутри, выходят наружу. Если нарушение принципа Low Coupling обычно представляют себе в виде переплетений из лапши, то здесь у меня в голове всплывает другое кулинарное блюдо. Позаимствовав терминологию у Java-разработчиков, если отдельный компонент - это фасолинка (Bean) - цельная, замкнутая вещь в себе, то тут мы получаем что-то вроде рагу, где фасоль полопалась и его содержимое вытекло, образовав густую однородную кашу, обволакивающую всю систему целиком, и не позволяющую на нее смотреть как на композицию отдельных законченных и слабо-зависимых сущностей.
Множество Boilerplate кода
Ну и нельзя не упомянуть то, за что Redux не ругал разве что ленивый. Мало того, что даже небольшое изменение функционала может потребовать относительно большие изменения в коде, так еще и код этот однотипный и не несущий никакой полезной для данной конкретной задачи нагрузки. По идее однотипный подход и код, над которым не надо думать, должен ускорять разработку, но лично меня это скорее утомляет, а эффект получается прямо противоположным. И да, инструменты, направленные на решение этой проблемы, существуют, но как по мне - не решают ее полностью.
Неуместное использование
А еще мне не нравится, что Redux или схожие с ним инструменты пытаются использовать там, где они не нужны - скажем, в Angular (angular-redux, NgRx). Redux предназначен для решения проблемы передачи данных в компоненты путем использования глобального State, и в React.js действительно существует такая проблема, там его использование кажется уместным. Но в Angular такой проблемы нет, Injectable-сервисы прекрасно справляются с этой задачей. Зачем решать несуществующую проблему, порождая при этом новые (о которых было написано выше)?
Ну и если говорить о неуместном использовании, также неуместным кажется использования Redux для управления локальным стейтом, что встречается довольно часто.
В заключение надо бы отметить и что-нибудь хорошее. Допустим, это не так плохо, когда у вас большая команда разработчиков разного уровня, и все пишут понятный всем код примерно в одном стиле ничего не выдумывая. Если иначе такая разношерстная команда не сможет работать эффективно, значит овчинка выделки определенно стоит. Но на самом деле хотелось бы иметь простое и эффективное решение, лишенное вышеназванных недостатков. Context API? Может быть.
dopusteam
О чем речь вообще?
О чем конкретно речь?
Почему не решают? Какие инструменты?
Это проблема инструмента?
Скорее уж rxjs, injectable и стейт так себе связаны, Вы точно понимаете о чем речь?
pharrell
Injectable — часть системы Dependency Injection, которая в свою очередь имеет IoC Container, который в свою очередь является глобальным состоянием приложения.
А вот RxJS тут как раз ни при чём:)
nin-jin
В Ангуляре на самом деле используется дерево контекстов (Injector), а не один глобальный контейнер.
dopusteam
Все стейт менеджеры на ангуляре основаны на rxjs, даже если Вы свой будете делать - это будут сервисы с rxjs
Что? Есть ссылки, подтверждающие это?
nin-jin
MobX не основан на RxJS. Хотя и прикручивается через недокументированные костыли.
saaivs
Собственно, сама документация angular.io/guide/dependency-injection
Никакого отношения Angular DI сам по себе к rxjs не имеет.
DI — это иерархия контейнеров, начиная от от глобального состояния и вплоть до локального состояния отдельного компонента, с общим единым подходом. По большому счёту, весь Angular — это просто реализация фреймворка поверх DI.
А rxjs, в свою очередь, — это уже существенная часть фреймворка.
Вот, например, одна из его standalone имплементаций ( от одного из представителей Angular Core Team) www.npmjs.com/package/injection-js, которую можно использовать где-угодно и там явно видно, что никаких внешних зависимостей(кроме tslib) у реализации DI нет.
На основе подобной библиотеки можно строить свои фреймворки, к Angular отношения не имеющие.
dopusteam
Я там про состояние ничего не увидел
Я обратного не утверждал. Я не согласен с идеей, что «ангуляру не нужен redux т.к. в ангуляре есть DI», изначально как то так звучала мысль
С остальным я согласен, да в общем и не спорил
saaivs
Не увидеть — не значит, что этого там нет. Состояние — это просто область памяти специфического назначения. Сервисы, в сочетании с rxjs и без оного, прекрасно реализуют работу с состоянием. Но и не только они. В целом, состояние — это неотъемлемый атрибут любой машины Тьюринга и поэтому присутствует везде. Даже если это слово не употебляется. Явной модели управления состоянием фреймворк не навязывает(хоть и намекает на неё).
Мысль, что «ангуляру не нужен redux т.к. в ангуляре есть DI» странна сама по себе. DI — это про управление зависимостями. Redux — про управление потоками данных. DI для angular — это фундамент, поверх которого он построен.
Redux — как способ организации потока данных абсолютно опционален. Он не нужен(или нужен) не потому, что есть DI, а потому, что есть масса и других способов организовать управление состоянием хоть с использованием DI, хоть без него.
dopusteam
Ну это уже демагогия пошла. IoC контейнер всё ещё не является 'состоянием' в общем смысле.
Ну так мысль то не моя, я изначально про неё и высказался, в оригинале
Я нисколько не топлю ни за redux ни против, более того — я им даже не пользуюсь
saaivs
О чём и речь.
YSpektor Автор
Ээ, нет, мысль звучала не совсем так
dopusteam
Вот же, нет?
YSpektor Автор
Все верно, только здесь написано не так, как меня «произвольно процитировали». Написано что:
1) Redux решает проблему передачи данных в компоненты, в случаях где нам неудобно/невозможно/нерационально использовать пропсы
2) В Angular аналогичную задачу принято решать с использованием сервисов, объявляемых с помощью Injectable (и соответственно через механизм DI внедряемых в компоненты — это не написано, но действительно подразумевается)
Здесь не написано что DI это замена Redux, но возможно мне нужно четче формулировать свои мысли
AxisPod
Явный троллинг
Ещё более явный троллинг
Опять же сродни троллинга
Это проблема API и ещё какая. Если уж инструмент не предназначен для чего либо, то и желательно как-то ограничить API. Опять же документация и внятное описание для решения каких проблем инструмент подходит. Топором тоже можно забить гвоздь, но ведь молотком удобнее.
Видимо понимает и ещё как. Делается отдельный сервис хранилище, отвечающий за конкретные данные. И пользуйтесь им на здоровье, в совокупности c RxJS вообще будет отличное решение. Да при этом данное решение ещё и легко тестируется.
MaZaAa
Для тех, кто любит когда кровь из глаз течёт глядя на такой код да, вообще отличное решение :D
А если ещё и придётся через недельку другую к этому коду вернуться и что-то там пофиксить или добавить фичу, вообще сказочное самовозгорание произойдет :D
Люблю тогда такой «код» пишут, сразу столько проектов на рынке появляется которые нужно переписать с чистого листа по человечески, сказка.