Посмотрел статьи. В целом методика весьма схожая. Есть отличия в деталях. Но в целом весьма похоже на комплекс тренировок в Илике без восточных терминах. Но работу это все равно не отменяет. Да за одно занятие можно почувствовать себя лучше. Но уж точно не избавиться ото всех проблем.
для меня проблема не понимание и даже не ощущение. а именно правильное приведение усилия. с учетом большого количества одновременных координаций. Как игра на барабанах. Стучать просто, но стучать правильно — сложно. имхо за 10 минут правильно выстроить все перекосы, которые накапливались годами — невозможно.
Да я бы не сказал, что восточные практики так сложно понять. Так просто эффект на мой вкус будет более долгосрочным и сразу есть группа единомышленников, чем просто сходить на один семинар без каждодневной работы на протяжении оставшихся лет. Принципы можно узнать за пару занятий. Но правильно проводить усилие, понимание того, как делать это правильно, требует гораздо больше времени.
Занимаюсь Илицюанем. Идеи положенные в основу те же. Расслабь поясницу, сбрось грудину и сделай усилие цельным. Одно могу сказать за несколоько месяцев занятий поясницу так и не расслабил, хотя в целом состояние спины значительно улучшилось. И практиковать нужно каждый день.
Вот посмотрел на код. Он понятен, все такое. НО! А тестить пробовали? Код, который не возвращает явного результата своей работы — читай имеет возвращаемый тип void checkUserPhone. Можно было бы вернуть инстанс измененного ProcessFlow. А так нам нужно дополнительно мочить используемые сущности. Грязные фукнции — зло.
Я понимаю циклические зависимости и все такое. Но как-то архитектура пахнет, сразу видны уши неграмотного архитектора одного из прошлых поколений, который связал вместе несколько модулей неправильно декомпозировав модули на части. Просто нужно разделять операции над данными от самих данных, тогда и циклических зависимостей не будет.
Плюс я бы на вашем месте все-таки внедрил бы flow или ts. Так хотя бы будет понятно какого типа у вас $flow и inputData. А так нужно постоянно проверять — есть ли нужное свойство, не свалимся с ошибкой. Плюс один глобальный объект to Rule them all — плохая практика. Все равно что в объекте request express держать глобальный объект data и пихать в него всю инфу. Опять же при запуске параллельном нескольких тестов — теряется главное свойство герметичность тестов. Т.е. чтобы один тест влиял на другие тесты.
Idef-0 Это ж те же самые reducers. Берите пример с фп и тестить проще
Собственно у нас так и есть. Все состояние лежит в NumberState.
Пусть читает данные кто хочет. Лишь бы не писали.
Однако немного модифицируем пример, чтобы обеспечить более строгую изоляцию:
class NumberState {
int get a;
int get b = 10;
setA(int a) {
this.a = a;
}
}
В итоге мы получили объект, который мы можем модифицировать, только если явно вызовем ту или иную функцию. Если и такой вариант не устраивает, то наворачиваем еще абстракции с иммутабельными стейтами, Stream, который доносит актуальный стейт в конструктор через подписку и т.д., и т.п.
Важное правило — читает любой, пишет только через внешние методы. О том, как лучше уже реализовывать эти внешние методы, это уже зависит от каждой конкретной команды. Кто-то на конвенциях может договориться. А кому-то нужна большая абстракция, чтобы максимально вывернуть руки.
Ведь за проброску данных отвечает DI и нам достаточно поправить вызов DI, а не копаться в тонне копипасты в коде, где используется new NumericOperation.
Да блин, хватит упарываться уже. К чему эта сугубая религиозность: ФП, ООП, это все мелочи. ФП и ООП это всего лишь инструменты для построения абстракции. Мы ж не спорим чо круче пила или молоток. ФП — небольшие функции для обработки состояния без сохранения состояния. ООП — хранение состояния системы во время всего жизненного цикла. Зачем противопоставлять эти концепции? Они ж взаимодополняют друг друга?
ФП всего лишь говорит вам — не хотите неопределенного поведения — уберите сайд-эффекты. Это не значит, что их не должно быть, просто они должны жить отдельно. Отделяйте операции над данными от самих данных. Я для себя выработал следующий подход: Обработка данных — класс, который через конструктор принимает объект с состоянием.
class NumericOperation {
NumberState state;
NumericOperation(this.state);
add() {
return state.a + state.b;
}
}
class NumberState {
int a;
int b;
}
Таким образом все состояние над которым мы производим операции можно протестировать, потому что мы его задаем только в конструкторе. Легко пользоваться моками. И да, несмотря на то, что NumericOperation хранит ссылку на состояние это не мешает нам писать в ФП стиле. Ведь функция add — чистая.
Для того, чтобы поддерживать систему и дальше. даже если она будет очень сложной — используйте Dependency Injection. Таким образом вы не будете сами инстанцировать классы, вы будете только указывать каким образом это сделать и какие зависимости должны быть прокинуты. Так можно расширять контракт без потери обратной совместимости на уровне входных данных. Мы не должны зависеть от атомарных типов. Мы должны зависеть от абстракций.
Любое состояние даже UI лучше хранить на клиенте. Т.к. вы можете в любой момент обратиться к нему, узнать чему оно равно, и отрендерить на его основе другой виджет. Плюс так гораздо проще сериализовывать. Надо помнить, что Angular 2 в первую очередь помогает рендерить верстку на основании данных. А не управлять данными на основе верстки. Это позволяет держать только один источник изменениий и уменьшает количество багов на клиенте.
Динамические require — адовое зло.
Я понимаю циклические зависимости и все такое. Но как-то архитектура пахнет, сразу видны уши неграмотного архитектора одного из прошлых поколений, который связал вместе несколько модулей неправильно декомпозировав модули на части. Просто нужно разделять операции над данными от самих данных, тогда и циклических зависимостей не будет.
Плюс я бы на вашем месте все-таки внедрил бы flow или ts. Так хотя бы будет понятно какого типа у вас $flow и inputData. А так нужно постоянно проверять — есть ли нужное свойство, не свалимся с ошибкой. Плюс один глобальный объект to Rule them all — плохая практика. Все равно что в объекте request express держать глобальный объект data и пихать в него всю инфу. Опять же при запуске параллельном нескольких тестов — теряется главное свойство герметичность тестов. Т.е. чтобы один тест влиял на другие тесты.
Idef-0 Это ж те же самые reducers. Берите пример с фп и тестить проще
https://github.com/zerkalica/reactive-di
Чоб это не заменить на настоящий вызов метода с декоратором или создать целый объект для обработки?
Пусть читает данные кто хочет. Лишь бы не писали.
Однако немного модифицируем пример, чтобы обеспечить более строгую изоляцию:
В итоге мы получили объект, который мы можем модифицировать, только если явно вызовем ту или иную функцию. Если и такой вариант не устраивает, то наворачиваем еще абстракции с иммутабельными стейтами, Stream, который доносит актуальный стейт в конструктор через подписку и т.д., и т.п.
Важное правило — читает любой, пишет только через внешние методы. О том, как лучше уже реализовывать эти внешние методы, это уже зависит от каждой конкретной команды. Кто-то на конвенциях может договориться. А кому-то нужна большая абстракция, чтобы максимально вывернуть руки.
Ведь за проброску данных отвечает DI и нам достаточно поправить вызов DI, а не копаться в тонне копипасты в коде, где используется new NumericOperation.
ФП всего лишь говорит вам — не хотите неопределенного поведения — уберите сайд-эффекты. Это не значит, что их не должно быть, просто они должны жить отдельно. Отделяйте операции над данными от самих данных. Я для себя выработал следующий подход: Обработка данных — класс, который через конструктор принимает объект с состоянием.
Таким образом все состояние над которым мы производим операции можно протестировать, потому что мы его задаем только в конструкторе. Легко пользоваться моками. И да, несмотря на то, что NumericOperation хранит ссылку на состояние это не мешает нам писать в ФП стиле. Ведь функция add — чистая.
Для того, чтобы поддерживать систему и дальше. даже если она будет очень сложной — используйте Dependency Injection. Таким образом вы не будете сами инстанцировать классы, вы будете только указывать каким образом это сделать и какие зависимости должны быть прокинуты. Так можно расширять контракт без потери обратной совместимости на уровне входных данных. Мы не должны зависеть от атомарных типов. Мы должны зависеть от абстракций.