То, что вы описываете, в целом то и похоже на акторную модель. Акторы так же, как у вас компоненты, запускаются и ждут сообщений. Вы можете запустить определенные акторы, которые должны ожидать сообщений постоянно, а можете при получении потока данных динамически запустить много акторов, которые чисто обработают эти данные и завершатся.
Вопрос только в описании графа перетекания данных между акторами. В целом, можно это сделать в декларативном виде.
И я согласен, что это удобный подход для проектирования системы на верхнем уровне. Под неудобством я имел в виду, что внутренняя реализация акторов как раз является более простым кодом и там уже на каждый чих типа вывести "Hello world" в консоль продолжать думать потоками данных - это уже оверхед.
Концепция интересная: каждая следующая команда имеет на входе результат предыдущей , поэтому вместо обычной записи c(b(a())) можем иметь более наглядную а -> b -> c
Так это обычный pipe-оператор, который является одной из основных синтаксических конструкций в таких языках как Elixir и F#
На том же Elixir есть Flow - библиотека для потоковой обработки. Вот например, как посчитать частоту употребления слов в текстовом документе:
path_to_file
# потоковое чтение из файла
|> File.stream!()
# использовать IO-поток в качестве продьюсера для Flow
|> Flow.from_enumerable()
# разбить каждую строку по пробельным символам
|> Flow.flat_map(fn(line) -> String.split(line, ~r/\s+/) end)
# распараллелить дальнейшую обработку на несколько потоков
|> Flow.partition()
# отфильтровать то, что не является словами
|> Flow.filter(fn(word) -> Regex.match?(~r/^[\w-]+$/iu, word) end)
# выполнить свёртку с подсчётом слов
|> Flow.reduce(fn -> %{} end, fn(word, acc) ->
Map.update(acc, word, 1, fn(count) -> count + 1 end)
end)
# преобразовать в список - реальные вычисления инициируются тут
|> Enum.to_list()
# отсортировать результат по убыванию частотности слов
|> Enum.sort(fn({_w1, count1}, {_w2, count2}) -> count1 >= count2 end)
А как эту же задачу решить на Neva?
P.S. Другое дело, что несмотря на удобство pipe-оператора писать 100% кода только в этом стиле - это неудобно. Поэтому глупо весь язык под это затачивать.
Могу подтвердить, что HRы и нанимающие менеджеры обсуждают почти всегда только gross, так что он имеется в виду по умолчанию.
Сотрудники между собой может и обсуждают "на руки", но мы тут обсуждаем что сказала руководитель команды IT-рекрутинга. К гадалке не ходи, она говорила про gross. Тут вообще без вариантов.
Она написала что это потолок для лида. Откуда следует что там есть выше?
Бывают такие случаи, когда компания не нанимает выше середины вилки.
Ну, раз уж вы выбрали Коми, то вот вам варианты до 5 млн в местной столице на ДомКлик
Понятно, что автор исходного коммента несколько утрировал ситуацию, но и вы зачем-то додумали, что речь про Москву. А Россия за садовым кольцом не заканчивается.
Давайте проверим, только не со слов (не будем опираться на личный опыт), а источником: Хабр Карьера. Результат перед вами.
Хабр Карьера показывает нетто, а в разговорах HR всегда говорят о брутто.
На скриншоте видно, что наибольшее кол-во джунов (80%) попали в диапазон 73.5 - 140 т.р. брутто.
Ну, т.е. корректнее было сказать, что вилка для начинающих специалистов остается в пределах 70-140 т.р. за редким исключением.
Так ли велика проблема, что Анастасия промахнулась на 10 т.р. при размахе диапазона в 70 т.р.? По-моему нет, тем более что от компании к компании, а также от стека (для той же Java диапазон 90-160k gross) это может варьироваться.
Что ж ты сидишь страдаешь на таком плохом Хабре? Понятно, что на зарубежных форумах тебе прямым текстом не скажут, что ты токсичный дурачок с раздутым самомнением. Там толерантность такая, что нельзя ничего напрямую сказать.
А тут стоит твою карму посмотреть и уже всё понятно, вот ты и бесишься. По существу ничего ответить не можешь, только щёки раздуваешь «вот я, вот я - один д’Артаньян, а вы все думать не умеете» xD
P.S. Недостойно твоё подленькое поведение, чтобы с тобой на "Вы" общаться. Смирись. Ну, или меняйся к лучшему, чтобы с тобой можно было цивилизованную беседу вести.
Ну, прижали тебя по фактам, имей мужество признать свою ошибку. Но нет, извивается как уж на сковородке, лишь бы не признавать очевидного 🤦♂️
Сходи на elixirforum со своим кодом, там тебе точно так же скажут, что не надо код так писать. Нет, как наивный чудик, бегает по чужим веткам комментариев, пыжясь доказать, что кроме него никто думать не умеет. Просто пробиваешь новое дно с каждым твоим комментарием.
Вы так эпично обосрались с чтением книги вашего любимого автора, что теперь следите за всеми моими комментариями? xD
Я в отличии от вас освоил навык декомпозиции и уже больше 18 лет его применяю. А вы застряли на уровне джуновского пика глупости, и пытаетесь себя самого подсознательно убедить, что ваши простыни на сотни строк - это что-то приемлемое. Но нет, ваш говнокод останется говнокодом, сколько вы тут не пыжьтесь)
Однако, это будет не декомпозицией метода, а написание вообще отдельного функционала на уровне библиотеки или фреймворка.
Ну да, хороший фреймворк тем и отличается, что "из коробки" предусматривает возможность декомпозиции для популярных сценариев.
В вашем примере так же копируется строчка с render.
Да. Просто когда это же выражено более длинной записью `$this->responseFactory->createJsonResponse` это выглядит, как визуальный шум. В современном PHP есть возможность указать делегирование? Что мы, вызывая в контексте своего класса функцию createJsonResponse, автоматически делегируем обработку этого вызова в $this->responseFactory
В каких именно случаях нам передавать необязательный параметр? Что будет если я его не передам когда надо и передам, когда не надо (изменю дефолтное значение)?
Это всё элементарно решается: принимаем и мержим этот параметр в ответ вне зависимости от статуса. И указываем для него в качестве дефолтного значения пустой массив. Когда хочешь добавить пейлоада в ответ или переопределить дефолтное сообщение, тогда и передаёшь.
А именно метод buildLoginRespons не совсем соответствует принципу SRP т.к. занимается отправкой и удачных и ошибочных ответов.
Занимается отправкой ответов - это одна обязанность. Для браузера абсолютно параллельно какой там статус, так что странно их разделять на успешные и нет. И вообще хорошо, когда она совсем абстрагирована на уровне фреймворка, и функция render импортирована в твой контроллер. Тогда бы не пришлось бойлерплейт вида $this->responseFactory->createJsonResponseпостоянно дублировать и генерация ответа спокойно в 1 строку умещалась, а не в 4.
Тоже самое касается и валидации параметров. Она должна быть по-хорошему быть выделена за пределы экшена контроллера. И возвращать не boolean, а осмысленный список ошибок валидации, который можно сразу на фронт передать. И в идеале, чтобы была возможность её вообще в отдельную миддлевару вынести, тогда бы login сразу принимал валидные параметры.
И было бы что-то типа
public function login($params): ResponseInterface
{
// Authenticate user
$authResult = $this->authService->authenticate($params['username'], $params['password']);
if ($authResult->isSuccess()) {
return render('status' => 200, 'json' => ['token' => $authResult->getToken()]);
} else {
return render('status' => 401, 'json' => ['error' => 'Invalid credentials']);
}
}
Другими словами, изначально длинная функция не является необходимостью. Она является следствием 2 вещей: 1) непродуманной организации фреймворка 2) недостаточной выразительности языка
Так же странным выглядит момент с resp_params который вроде обязательный аргумент
Очевидно же из контекста, что он опциональный. Я просто не помню, как в PHP опциональные аргументы объявляются. И в явном виде об этом написал в комментарии.
У вас странное представление о декомпозиции. Изначальная функция занималась разбором параметров, их валидацией, аутентификацией по логину и паролю и формированием HTTP-ответа. Вот по этим аспектам и надо разделять, а не тупо каждую строку в отдельную функцию выделять.
public function login(ServerRequestInterface $request): ResponseInterface
{
$params = $request->getParsedBody();
// Validate request parameters
if (!$isValidLoginParameters($params)) {
return buildLoginResponse(400);
}
// Authenticate user (мы ж уже проверили, что всё валидно, зачем ещё раз на null проверять?)
$authResult = $this->authService->authenticate($params['username'], $params['password']);
if ($authResult->isSuccess()) {
return buildLoginResponse(200, ['token' => $authResult->getToken()]);
} else {
return buildLoginResponse(401);
}
}
function isValidLoginParameters($params): boolean
{
$isValidParameters = $this->validator->isValid($params, [
'username' => 'required|string',
'password' => 'required|string',
]);
return $isValidParameters;
}
function buildLoginResponse($code, $resp_params): ResponseInterface
{
switch ($code) {
case 200:
return $this->responseFactory->createJsonResponse(
200,
array_merge(['message' => 'Login successful'], $resp_params)
);
case 400:
return $this->responseFactory->createJsonResponse(
400,
['error' => 'Invalid request parameters']
);
case 401:
return $this->responseFactory->createJsonResponse(
401,
['error' => 'Invalid credentials']
);
}
}
P.S. Возможны ошибки синтаксиса, т.к. я в прошлый раз на PHP писал в 2009-м, но суть идеи, думаю, понятна.
Я вам фрагмент из книги предоставил, в котором прямым текстом написано, что ваш подход к структурированию кода является типичной ошибкой. При этом вы самонадеянно утверждали, что там не может быть такого написано. Но признать, что вы облажались, у вас силы духа не хватает. Вот и пытаетесь нелепо съехать с темы, переходя на личности. Это ещё один тупой мув в стиле джуна, потому что вы обо мне ничего не знаете. И выглядите во всей этой ветке максимально тупо и нелепо xD
Если мы хотим последовательно, то будут последовательно, хотим параллельно - будут параллельно.
Я уже выше приводил пример с Flow. Но можно и ещё проще, например, хотите в 10 параллельных потоков события обработать, вот пожалуйста:
Другими словами, когда надо - распараллеливаете, когда надо - схлопываете снова в 1 поток.
Подробнее о базовых элементах: Task, Stream
И о самом потоковом подходе: GenStage и Flow
То, что вы описываете, в целом то и похоже на акторную модель. Акторы так же, как у вас компоненты, запускаются и ждут сообщений. Вы можете запустить определенные акторы, которые должны ожидать сообщений постоянно, а можете при получении потока данных динамически запустить много акторов, которые чисто обработают эти данные и завершатся.
Вопрос только в описании графа перетекания данных между акторами. В целом, можно это сделать в декларативном виде.
И я согласен, что это удобный подход для проектирования системы на верхнем уровне. Под неудобством я имел в виду, что внутренняя реализация акторов как раз является более простым кодом и там уже на каждый чих типа вывести "Hello world" в консоль продолжать думать потоками данных - это уже оверхед.
Что вы под этим подразумеваете? Pipe-operator?
Так это обычный pipe-оператор, который является одной из основных синтаксических конструкций в таких языках как Elixir и F#
На том же Elixir есть Flow - библиотека для потоковой обработки. Вот например, как посчитать частоту употребления слов в текстовом документе:
А как эту же задачу решить на Neva?
P.S. Другое дело, что несмотря на удобство pipe-оператора писать 100% кода только в этом стиле - это неудобно. Поэтому глупо весь язык под это затачивать.
Да, можно вместо th расслаблено произносить d и носители вас прекрасно поймут.
А в чём смысл этой статьи? Вы хотя бы небольшой обзор языка сделали бы, с примерами и т.д.
Могу подтвердить, что HRы и нанимающие менеджеры обсуждают почти всегда только gross, так что он имеется в виду по умолчанию.
Сотрудники между собой может и обсуждают "на руки", но мы тут обсуждаем что сказала руководитель команды IT-рекрутинга. К гадалке не ходи, она говорила про gross. Тут вообще без вариантов.
Бывают такие случаи, когда компания не нанимает выше середины вилки.
Ну, раз уж вы выбрали Коми, то вот вам варианты до 5 млн в местной столице на ДомКлик
Понятно, что автор исходного коммента несколько утрировал ситуацию, но и вы зачем-то додумали, что речь про Москву. А Россия за садовым кольцом не заканчивается.
Так и есть. 2 месяца назад с ним подобную переписку вёл https://habr.com/ru/articles/855102/comments/#comment_27568354
Смотрю комменты к этой статье, а воз и ныне там(
Так он и не писал про Мск. Например, где-нибудь в Бологое 2 однушки вполне реально взять за год.
Хабр Карьера показывает нетто, а в разговорах HR всегда говорят о брутто.
На скриншоте видно, что наибольшее кол-во джунов (80%) попали в диапазон 73.5 - 140 т.р. брутто.
Ну, т.е. корректнее было сказать, что вилка для начинающих специалистов остается в пределах 70-140 т.р. за редким исключением.
Так ли велика проблема, что Анастасия промахнулась на 10 т.р. при размахе диапазона в 70 т.р.? По-моему нет, тем более что от компании к компании, а также от стека (для той же Java диапазон 90-160k gross) это может варьироваться.
Ну, естественно это джун, а кто ж ещё. Откуда взялась мода думать, что джун - это плохой/некомпетентный сотрудник? Джун - это начинающий специалист.
А почему без опыта? До 2 лет опыта - это тоже джун (начинающий специалист).
Что ж ты сидишь страдаешь на таком плохом Хабре? Понятно, что на зарубежных форумах тебе прямым текстом не скажут, что ты токсичный дурачок с раздутым самомнением. Там толерантность такая, что нельзя ничего напрямую сказать.
А тут стоит твою карму посмотреть и уже всё понятно, вот ты и бесишься. По существу ничего ответить не можешь, только щёки раздуваешь «вот я, вот я - один д’Артаньян, а вы все думать не умеете» xD
P.S. Недостойно твоё подленькое поведение, чтобы с тобой на "Вы" общаться. Смирись. Ну, или меняйся к лучшему, чтобы с тобой можно было цивилизованную беседу вести.
Ну, прижали тебя по фактам, имей мужество признать свою ошибку. Но нет, извивается как уж на сковородке, лишь бы не признавать очевидного 🤦♂️
Сходи на elixirforum со своим кодом, там тебе точно так же скажут, что не надо код так писать. Нет, как наивный чудик, бегает по чужим веткам комментариев, пыжясь доказать, что кроме него никто думать не умеет. Просто пробиваешь новое дно с каждым твоим комментарием.
Вы так эпично обосрались с чтением книги вашего любимого автора, что теперь следите за всеми моими комментариями? xD
Я в отличии от вас освоил навык декомпозиции и уже больше 18 лет его применяю. А вы застряли на уровне джуновского пика глупости, и пытаетесь себя самого подсознательно убедить, что ваши простыни на сотни строк - это что-то приемлемое. Но нет, ваш говнокод останется говнокодом, сколько вы тут не пыжьтесь)
Ну да, хороший фреймворк тем и отличается, что "из коробки" предусматривает возможность декомпозиции для популярных сценариев.
Да. Просто когда это же выражено более длинной записью `$this->responseFactory->createJsonResponse` это выглядит, как визуальный шум. В современном PHP есть возможность указать делегирование? Что мы, вызывая в контексте своего класса функцию createJsonResponse, автоматически делегируем обработку этого вызова в $this->responseFactory
Это всё элементарно решается: принимаем и мержим этот параметр в ответ вне зависимости от статуса. И указываем для него в качестве дефолтного значения пустой массив. Когда хочешь добавить пейлоада в ответ или переопределить дефолтное сообщение, тогда и передаёшь.
Занимается отправкой ответов - это одна обязанность. Для браузера абсолютно параллельно какой там статус, так что странно их разделять на успешные и нет. И вообще хорошо, когда она совсем абстрагирована на уровне фреймворка, и функция render импортирована в твой контроллер. Тогда бы не пришлось бойлерплейт вида
$this->responseFactory->createJsonResponseпостоянно дублировать и генерация ответа спокойно в 1 строку умещалась, а не в 4.Тоже самое касается и валидации параметров. Она должна быть по-хорошему быть выделена за пределы экшена контроллера. И возвращать не boolean, а осмысленный список ошибок валидации, который можно сразу на фронт передать. И в идеале, чтобы была возможность её вообще в отдельную миддлевару вынести, тогда бы login сразу принимал валидные параметры.
И было бы что-то типа
Другими словами, изначально длинная функция не является необходимостью. Она является следствием 2 вещей:
1) непродуманной организации фреймворка
2) недостаточной выразительности языка
Очевидно же из контекста, что он опциональный. Я просто не помню, как в PHP опциональные аргументы объявляются. И в явном виде об этом написал в комментарии.
У вас странное представление о декомпозиции. Изначальная функция занималась разбором параметров, их валидацией, аутентификацией по логину и паролю и формированием HTTP-ответа. Вот по этим аспектам и надо разделять, а не тупо каждую строку в отдельную функцию выделять.
P.S. Возможны ошибки синтаксиса, т.к. я в прошлый раз на PHP писал в 2009-м, но суть идеи, думаю, понятна.
Я вам фрагмент из книги предоставил, в котором прямым текстом написано, что ваш подход к структурированию кода является типичной ошибкой. При этом вы самонадеянно утверждали, что там не может быть такого написано. Но признать, что вы облажались, у вас силы духа не хватает. Вот и пытаетесь нелепо съехать с темы, переходя на личности. Это ещё один тупой мув в стиле джуна, потому что вы обо мне ничего не знаете. И выглядите во всей этой ветке максимально тупо и нелепо xD