Мне нравится работать стоя, но устаёт спина и ноги. Я долго выбирал между коленным стулом для и стулом-седлом, в итоге прикупил вот такое рабочее место.
Подставка по ноги наклоняется, вся конструкция подпружинена и позовляет покачиваться. Ноги разгружены и спина тоже.
Но я так понимаю, это просто на уровне соглашения, так? Компилятор же никак не помешает переиспользовать любые ивенты в любых блоках.
Статический анализатор покажет что событие не в тот блок добавляем, здесь всё как с обычным блоком
Не обязательно, это вполне применимо и к классам. В вашем варианте блок знает про ивенты и стейт, ивенты знают про блок и стейт. В моем варианте блок знает про ивенты и стейт, но ивенты ничего не знают ни про блок, ни про стейт.
тут дело вкуса и сложно вынести из беседы в коментах какой либо артефакт =) предлагаю остановиться =)
А если в какой-то момент поведение станет специфичным для подтипа этого блока?
Я переиспользую только Input ивенты в FormBloc — сами ивенты только меняют свой инпут в стейте и более ничего — вряд ли что-то сильно измениться =)
Но проблема всё равно, на мой взгляд, остается. Просто это скорее про coupling/cohesion – с одной стороны, event знает слишком много про логику обработки, с другой – эта самая логика обработки оказывается размазанной по нескольким классам
Не согласен, coupling/cohesion применимы к модулям. В разрезе Event/Bloc классы ивентов и класс блока — это один модуль и они поумолчанию тесно связаны(coupling) «ивенты конкретного блока». Это сильно связанные классы для реализации одной какой-то логики/компонента.
Про зацепление(cohesion). Если бы я в Event/Bloc засылал BuildContext — это действительно было бы сильным зацеплением на Flutter framework, но такое разумеется пресекается.
(в т.ч. и в самом блоке логика будет – как иначе сделать, например, debounce ивентов?)
debounce сложной/долгой операции — это не логика фичи, это ограничения для жизни в реальной среде с нагрузками на сеть/cpu. Поэтому debounce делаю в Bloc.transformEvents разделяя Event и _DebouncedEvent, последний выполняет долгую/сложную/нагрузо_нежалательную операцию
Object currentState можно специфицировать дженериком, мне всё не начать рефакторинг
переиспользуемые ивенты знают только о родительском FormBloc, им не нужно знать в какой конкретный блок его отправляют, достаточно чтобы он был наследником
про нарушение SRP категорически не согласен
Про Команду
Если у команды есть нагрузка(ex. user input), то без класса сложно обойтись и как раз нагрузку в конструктор и укладываю, а сигнатура метода выполнения остаётся неизменной.
Про несогласие о нарушении SRP…
The Single Responsibility Principle (SRP) states that each software module should have one and only one reason to change.
Ивенты блока и сам блок — это один компонент логики Business Logic Component, который вполне себе отдельный software module.
Нарушен SRP если этот модуль могут попросить изменить к примеру и Аналитик, и UX'ксер
На каждом проекте люди играют разные роли (actor): Аналитик, Проектировщик интерфейсов, Администратор баз данных. Естественно, один человек может играть сразу несколько ролей. В этом принципе речь идет о том, что изменения в модуле может запрашивать одна и только одна роль. Например, есть модуль, реализующий некую бизнес-логику, запросить изменения в этом модуле может только Аналитик, но никак не DBA или UX.
С null всё понял, интутивно без манифестов в командах избегаем null и не так больно видать мне )
Про кодген — чаще в командах начинающие ребята и это накладывает ограничения.
Я использую свой подход к ивентам — делегировал им исполнение, получилось скрещение паттернов Состояние и Команда(ивенты у нас инстансы команд по сути)
каждый ивент имплементит метод,
abstract class BlocEvent {
Stream<Object> mapEventToState(Object currentState, B bloc);
текущий стейт специально добавлен чтобы не пользовать геттер bloc.state и получить доступ к стейту
if (state is ...) state.foo
Получается bloc является контекстом паттерна состояния и содержит только зависимости
class BarBloc extends BaseBloc<...,...> {
final Api api;
final Foo foo;
}
А BaseBloc делегирует исполнение ивентам, по сути являясь диспатчером очереди команд
Спасибо за статью, руковожусь таким же принципами, только избегаю codegen для стейтов/ивентов.
Есть комент к…
Следующий момент (я о нем уже упоминал, но он достоин того, чтобы по нему еще раз пройтись) – NoSuchMethodError (здравствуй, Java с ее NullPointerException). Говорят, скоро все будет хорошо, осталось всего лишь дождаться миграции самого Flutter'а и всех библиотек, но пока – что есть, то есть.
noSuchMethod актуален только при вызове на dynamiс, вызвать любой метод на null невозможно и нет никакой связи с Java NPE
Собственно прошу объяснить, вдруг это у меня лыжи не едут, либо поправить статью =)
Cделать приватным final BehaviorSubject onCounterUpd;
Наружу только Observable или Stream, потому что я могу миновать incrementCounter() и сунуть туда какое захочу value и оно минует вашу логику
Это как раз вопрос велосипеда, он уже несостоятелен и его приходится рихтовать.
Зачем здесь async функция? Future incrementCounter() async
The build method of a stateless widget is typically only called in three situations: the first time the widget is inserted in the tree, when the widget's parent changes its configuration, and when an InheritedWidget it depends on changes.
The implementation of this method must only depend on:
the fields of the widget, which themselves must not change over time
Не зная первой цитаты ты стреляешь себе в ногу в будущем(ближайшем) Не зная второй цитаты ты опираешься на внешние данные, полностью перечя ей (upd тут я неправ)
Ты не читал документацию или не перечитал с пониманием
Автор непонимает основ Rx и Flutter
Ошибки
1. инициализация значения в BehaviorSubject не через named конструктор .seeded(initValue)
2. эта ошибка вытекла из первой, но показала непонимание основ Flutter.
Нельзя ничего инициировать в методе build() любого типа виджета
закрепить на газлифте можно, но конструкция нежёсткая в силу дизайна и скорее, да, для ноубуков
у меня есть газлифт, но монитор я отдал при переезде, думаю побробовать позже
Мне нравится работать стоя, но устаёт спина и ноги. Я долго выбирал между коленным стулом для и стулом-седлом, в итоге прикупил вот такое рабочее место.
Подставка по ноги наклоняется, вся конструкция подпружинена и позовляет покачиваться.
Ноги разгружены и спина тоже.
Работаю чуть больше месяца и очень доволен
Статический анализатор покажет что событие не в тот блок добавляем, здесь всё как с обычным блоком
тут дело вкуса и сложно вынести из беседы в коментах какой либо артефакт =) предлагаю остановиться =)
Я переиспользую только Input ивенты в FormBloc — сами ивенты только меняют свой инпут в стейте и более ничего — вряд ли что-то сильно измениться =)
Не согласен, coupling/cohesion применимы к модулям. В разрезе Event/Bloc классы ивентов и класс блока — это один модуль и они поумолчанию тесно связаны(coupling) «ивенты конкретного блока». Это сильно связанные классы для реализации одной какой-то логики/компонента.
Про зацепление(cohesion). Если бы я в Event/Bloc засылал BuildContext — это действительно было бы сильным зацеплением на Flutter framework, но такое разумеется пресекается.
debounce сложной/долгой операции — это не логика фичи, это ограничения для жизни в реальной среде с нагрузками на сеть/cpu. Поэтому debounce делаю в Bloc.transformEvents разделяя Event и _DebouncedEvent, последний выполняет долгую/сложную/нагрузо_нежалательную операцию
Про Команду
Если у команды есть нагрузка(ex. user input), то без класса сложно обойтись и как раз нагрузку в конструктор и укладываю, а сигнатура метода выполнения остаётся неизменной.
Про несогласие о нарушении SRP…
Ивенты блока и сам блок — это один компонент логики Business Logic Component, который вполне себе отдельный software module.
Нарушен SRP если этот модуль могут попросить изменить к примеру и Аналитик, и UX'ксер
Хабр статья про SOLID
Ссылка на оригинал SRP от R. C. Martin
Посему не считаю SRP нарушенным =)
ps я как раз такого избегаю
Про кодген — чаще в командах начинающие ребята и это накладывает ограничения.
Я использую свой подход к ивентам — делегировал им исполнение, получилось скрещение паттернов Состояние и Команда(ивенты у нас инстансы команд по сути)
каждый ивент имплементит метод,
текущий стейт специально добавлен чтобы не пользовать геттер bloc.state и получить доступ к стейту
Получается bloc является контекстом паттерна состояния и содержит только зависимости
А BaseBloc делегирует исполнение ивентам, по сути являясь диспатчером очереди команд
Плюсы
В 2ух словах так)
Есть комент к…
noSuchMethod актуален только при вызове на dynamiс, вызвать любой метод на null невозможно и нет никакой связи с Java NPE
Собственно прошу объяснить, вдруг это у меня лыжи не едут, либо поправить статью =)
За статью спасибо!
минусатор, есть аргументированные возражения этому коменту?
Наружу только Observable или Stream, потому что я могу миновать incrementCounter() и сунуть туда какое захочу value и оно минует вашу логику
Это как раз вопрос велосипеда, он уже несостоятелен и его приходится рихтовать.
Зачем здесь async функция?
FutureincrementCounter()asyncЯ вношу вклад в сообщество помогая в чатике и критикуя явные проблемы, особенно если это публичная статья для новичков с грубыми ошибками.
BLoC & MVI смысл один один поток на вход, один на выходе
+ предсказуемый стейт
Вы пишете свой велосипед, который может потерять этот +, если не один единственный поток на вход(очередь событий)
Почитайте про синдром NIH
Жду статью про велосипед дериватив MVU(TEA), Redux, MVI, BLoC
UDF + Rx
Как тебе угодил MVI, но не угодил BLoC?
Intent -> Model -> View
Event -> State -> View
эквивалентные вещи и через тот же Rx
Вы в статье против BLoС, но вы не в курсе что flutter_bloc — это дериватив MVU(родоначальник подхода ELM TEA)
guide.elm-lang.org/architecture
MVI это то же дериватив MVU и бойлерплейта там тоже много
flutter_bloc, MVU(TEA), Redux, MVI эквивалентны (просто разные реализации)
Получается что пчёлы против мёда и ваши статьи противоречят вашему же коментарию выше
ошибки обозначены и описаны, показано что надо изменить.
Не нагнетайте
убери из конструктора
убери из StreamBuilder'а
api.flutter.dev/flutter/widgets/StatelessWidget-class.html
api.flutter.dev/flutter/widgets/StatelessWidget/build.html
Не зная первой цитаты ты стреляешь себе в ногу в будущем(ближайшем)
Не зная второй цитаты ты опираешься на внешние данные, полностью перечя ей(upd тут я неправ)Ты не читал документацию или не перечитал с пониманием
Ошибки
1. инициализация значения в BehaviorSubject не через named конструктор .seeded(initValue)
2. эта ошибка вытекла из первой, но показала непонимание основ Flutter.
Нельзя ничего инициировать в методе build() любого типа виджета