Вы так эпично обосрались с чтением книги вашего любимого автора, что теперь следите за всеми моими комментариями? 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
Программисты как раз пишут алгоритмы, а кодеры кодят бизнес-логику.
Это крайне наивный взгляд)
Особенно забавно, что вы всерьёз думаете, что вы какие-то принципиально новые алгоритмы придумываете для своих библиотек. Хотя просто кодируете уже давно придуманные, как заправский техник.
Исключения конечно возможны, но тогда хотя бы ссылочку на диссертацию свою скиньте - почитаем какую вы там научную новизну в алгоритмы привнесли.
Вы не баг подсветили, а отрезали пользователям задокументированную возможность использовать свой синтаксис
Хотели сказать "незадокументированную" xD
Ни в самом коде, ни в README нет ни явного указания на кастомный синтаксис, ни примеров его создания. Просто по сути ещё один аргумент накинули, почему ваш код плох - содержит незадокументированные и не покрытые тестами "возможности".
у меня такое количество коммитов в корку эликсира
Ну, раз начали понтоваться, то пишите уж ваш ник на Github - посмотрим, как вы в корку эликсира функции по 50+ строк пропихивали. А то пустое сотрясание воздуха.
Во-первых, это библиотека с кучей математики. Во-вторых, ещё и на чудовищно многословном С++. Ну ok, я уточню, математические библиотеки на С++ не пишут 99.9999% программистов
Зачем вы берёте такой махровый edge-кейс и пытаетесь его представить как нечто типовое - это загадка, конечно.
Суть в том, что написание математических библиотек - это очень узкий кейс. Называть его типовым крайне странно, т.к. 99.9% программистов такое не пишут вовсе.
А если вы хотели сказать, что бывают крайне редкие кейсы, когда длинные функции оправданы, то с этим никто собственно и не спорил.
В коммерческой разработке свои правила. Там не прокатит применять интерфейсы 30-летней давности. Разве что это заказная разработка для какой-то мелкой конторы, где вы демпингом контракт заполучили и им теперь сложно отказаться.
Да, серьёзно. Интерфейсы под Windows определяет Microsoft. Чтобы они выглядели нативно, будьте любезны следовать их рекомендациям.
А что вы чудовищного нашли в .NET программах непонятно. Речь идёт об обычных простеньких десктопных приложениях. Там .NET очевидно лучший выбор под Windows. Кажется, вы явно переоцениваете на порядок сложность ваших пет-проектов, раз считаете что C++ там жизненно необходим.
P.S. Почитайте книгу "Психбольница в руках пациентов". Там как раз подробно разбирается ситуация, когда программисты делают интерфейсы для себя, а не для пользователей.
Это безусловно так. Но случаев когда единый смысл занимает много строк - редки. Поэтому условный N используется в таком варианте: у тебя получилась функция из N+ строк, а точно ли она одно атомарное действие совершает или ты сам себя обманул?
Если действительно одно атомарное действие, то и ok. Но в 99.9% случаев это окажется не так при достаточно большом N (напр. 50).
так что ваши неуклюжие попытки затруднить мне ее развитие — идут прямо в корзину
Понятно, по существу сказать то и нечего xD
ей люди, знаете ли, пользуются;
Вот и поблагодарили бы хотя бы за то, что я вам баг подсветил. А то принимаете любой атом в качестве syntax, а пользователи ваши потом с дебагом маются из-за того, что кто-то не осилил валидацию параметров нормально сделать.
И да, МакКорд ничего про такой код в «Metaprogramming Elixir» не писал (да и не мог), потому что книжка вышла 10 лет назад
Вот уж наглядная демонстрация выражения "смотрю в книгу - вижу фигу". Перечитайте:
Avoid Injecting Large Amounts of Code
И дальше как раз идёт minimal viable example как из плохого кода по типу вашего сделать приличный код.
Самое интересное, что мне никогда не доводилось видеть работоспособный сложный код от пуристов подхода «все разбить на малюсенькие кусочки кода и разложить их по тремстам разным полочкам».
Жаль вас, ни разу в нормальной компании не довелось поработать. Структурирование кода - это важный навык для любого программиста.
Здесь уместно спросить: «Вам шашечки или ехать?». Я предпочитаю «ехать».
Это ложная дихотомия. Использовать современный UI-фреймворк ничуть не сложнее, чем UI-фреймворк старый как дерьмо мамонта. Наоборот, современные ещё и поудобнее будут.
Вот вам пример на 200+ строк (этим кодом пользуются самые разные люди, которые иногда просят что-то добавить, и если бы этот код был размазан по нескольким хелперам — я бы уже давно сдался и перестал бы что-то добавлять, а так каждое добавление — минут на 15 с тестами).
Да уж. Запихнуть прямо в quote столько строк - это полный ппц, даже для миддла. Вы бы книжку "Metaprogramming in Elixir" хоть бы прочитали. Там подробно описано, почему так не надо делать.
Держите рефакторинг, который сокращает ваш говнокод на 51 строку, и при этом читаемость возрастает существенно. Плюсом ещё и ошибку вам исправил при доставании syntax из options, и спеки для статического анализатора добавил. И тесты, разумеется, на отдельные небольшие функции писать гораздо проще, чем на одну огромную.
defmodule ASTHelpers do
@spec fetch_syntax(keyword() | map()) :: Finitomata.Mermaid | Finitomata.PlantUML
def fetch_syntax(options) do
case Keyword.fetch!(options, :syntax) do
:flowchart ->
Finitomata.Mermaid
:state_diagram ->
Finitomata.PlantUML
module when module in [Finitomata.Mermaid, Finitomata.PlantUML] ->
reporter().info([
[:yellow, "deprecated: ", :reset],
"using built-in modules as syntax names is deprecated, please use ",
[:blue, ":flowchart", :reset],
" and/or ",
[:blue, ":state_diagram", :reset],
" instead"
])
module
invalid_value ->
raise ArgumentError, "Invalid value of syntax opt: #{inspect(invalid_value)}"
end
end
@spec fetch_listener(keyword() | map(), atom()) :: Finitomata.Listener.t() | Mox.t()
def fetch_listener(options, module) do
mox_envs = options |> Keyword.fetch!(:mox_envs) |> List.wrap()
case Keyword.fetch!(options, :listener) do
:mox -> if Mix.env() in mox_envs, do: def_mock_for(module)
{:mox, listener} -> if Mix.env() in mox_envs, do: def_mock_for(module), else: listener
{listener, :mox} -> if Mix.env() in mox_envs, do: def_mock_for(module), else: listener
listener -> listener
end
end
defp def_mock_for(module) do
with {:error, error} <- Code.ensure_compiled(Mox) do
reporter().info([
[:yellow, "expectation: ", :reset],
"to be able to use ",
[:blue, ":mox", :reset],
" listener in tests with ",
[:blue, "`Finitomata.ExUnit`", :reset],
", please add ",
[:blue, "`{:mox, \"~> 1.0\", only: [:test]}`", :reset],
" as a dependency to your ",
[:blue, "`mix.exs`", :reset],
" project file (got: ",
[:yellow, inspect(error), :reset],
")"
])
end
[module, Mox] |> Module.concat() |> tap(&Mox.defmock(&1, for: Finitomata.Listener))
end
defp reporter do
if Code.ensure_loaded?(Mix), do: Mix.shell(), else: Logger
end
end
И это я только первые 73 строки прочитал и из них осталось только 22. Потому что, когда я опции разбираю, я хочу видеть логику разбора каждой конкретной опции инкапсулированной в отдельную функцию, а не общую спагетти-мешанину.
И в дальнейших раундах рефакторинга можно и весь последующий код сделать гораздо более удобоваримым.
Я его привел для тех, кто сможет экстраполировать. Это называется minimal viable example, если вы не способны — это ещё не значит, что вокруг все такие же.
Your hobby is extrapolating
Your hobby is extrapolating
Ну, видите, как интересно. Более реальный пример наглядно показал, что вы пока что просто плохо умеете программировать. Желаю вам в этом году повысить свой профессиональный уровень, а то подрываете мне тут имидж программистов на Elixir. Ая-яй-яй.
ТоталКоммандер вообще на Delphi, насколько я помню, также как и классический Skype
А идея оценивать удобство ЯП по скриншотам GUI звучит так, что вы знаете толк в извращениях. Тем более, что судя по вашим скриншотам, вы совсем какой-то древний UI-фреймворк используете. Даже 20 лет назад такой интерфейс выглядел устаревшим как какая-то программа времён Windows 95.
Оригинальное число, которое мы тут обсуждаем — 50. И только если это одно атомарное действие. (Это цитата из моего первого комментария, стартовавшего ветку.)
Вы не сможете придумать атомарное действие, нуждающееся в тысячах или десятках тысячах строк для реализации.
Так вы сами не смогли придумать атомарное действие, нуждающееся в 50 строках. И зачем то привели пример из 2 строк. Максимально неконструктивный троллинг.
Вы так эпично обосрались с чтением книги вашего любимого автора, что теперь следите за всеми моими комментариями? 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
Это крайне наивный взгляд)
Особенно забавно, что вы всерьёз думаете, что вы какие-то принципиально новые алгоритмы придумываете для своих библиотек. Хотя просто кодируете уже давно придуманные, как заправский техник.
Исключения конечно возможны, но тогда хотя бы ссылочку на диссертацию свою скиньте - почитаем какую вы там научную новизну в алгоритмы привнесли.
Бла-бла-бла от очередного джуна на пике глупости. Не интересно время больше тратить на то, чтобы вам ликбез проводить.
Хотели сказать "незадокументированную" xD
Ни в самом коде, ни в README нет ни явного указания на кастомный синтаксис, ни примеров его создания. Просто по сути ещё один аргумент накинули, почему ваш код плох - содержит незадокументированные и не покрытые тестами "возможности".
Ну, раз начали понтоваться, то пишите уж ваш ник на Github - посмотрим, как вы в корку эликсира функции по 50+ строк пропихивали. А то пустое сотрясание воздуха.
И как это противоречит тому, что я написал?
Во-первых, это библиотека с кучей математики. Во-вторых, ещё и на чудовищно многословном С++.
Ну ok, я уточню, математические библиотеки на С++ не пишут 99.9999% программистов
Зачем вы берёте такой махровый edge-кейс и пытаетесь его представить как нечто типовое - это загадка, конечно.
Суть в том, что написание математических библиотек - это очень узкий кейс. Называть его типовым крайне странно, т.к. 99.9% программистов такое не пишут вовсе.
А если вы хотели сказать, что бывают крайне редкие кейсы, когда длинные функции оправданы, то с этим никто собственно и не спорил.
Ну, у вас свой путь - хобби-программирование.
В коммерческой разработке свои правила. Там не прокатит применять интерфейсы 30-летней давности. Разве что это заказная разработка для какой-то мелкой конторы, где вы демпингом контракт заполучили и им теперь сложно отказаться.
Да, серьёзно. Интерфейсы под Windows определяет Microsoft. Чтобы они выглядели нативно, будьте любезны следовать их рекомендациям.
А что вы чудовищного нашли в .NET программах непонятно. Речь идёт об обычных простеньких десктопных приложениях. Там .NET очевидно лучший выбор под Windows. Кажется, вы явно переоцениваете на порядок сложность ваших пет-проектов, раз считаете что C++ там жизненно необходим.
P.S. Почитайте книгу "Психбольница в руках пациентов". Там как раз подробно разбирается ситуация, когда программисты делают интерфейсы для себя, а не для пользователей.
Это безусловно так. Но случаев когда единый смысл занимает много строк - редки. Поэтому условный N используется в таком варианте: у тебя получилась функция из N+ строк, а точно ли она одно атомарное действие совершает или ты сам себя обманул?
Если действительно одно атомарное действие, то и ok. Но в 99.9% случаев это окажется не так при достаточно большом N (напр. 50).
Понятно, по существу сказать то и нечего xD
Вот и поблагодарили бы хотя бы за то, что я вам баг подсветил. А то принимаете любой атом в качестве syntax, а пользователи ваши потом с дебагом маются из-за того, что кто-то не осилил валидацию параметров нормально сделать.
Вот уж наглядная демонстрация выражения "смотрю в книгу - вижу фигу". Перечитайте:
И дальше как раз идёт minimal viable example как из плохого кода по типу вашего сделать приличный код.
Жаль вас, ни разу в нормальной компании не довелось поработать. Структурирование кода - это важный навык для любого программиста.
Под «Форточки» вам .NET как эталон интерфейсов надо рассматривать, вот обзор UI-фреймворков под него: https://www.youtube.com/watch?v=ze8o-Qa3v7Y
Это ложная дихотомия. Использовать современный UI-фреймворк ничуть не сложнее, чем UI-фреймворк старый как дерьмо мамонта. Наоборот, современные ещё и поудобнее будут.
Да уж. Запихнуть прямо в quote столько строк - это полный ппц, даже для миддла. Вы бы книжку "Metaprogramming in Elixir" хоть бы прочитали. Там подробно описано, почему так не надо делать.
Держите рефакторинг, который сокращает ваш говнокод на 51 строку, и при этом читаемость возрастает существенно. Плюсом ещё и ошибку вам исправил при доставании syntax из options, и спеки для статического анализатора добавил. И тесты, разумеется, на отдельные небольшие функции писать гораздо проще, чем на одну огромную.
И это я только первые 73 строки прочитал и из них осталось только 22. Потому что, когда я опции разбираю, я хочу видеть логику разбора каждой конкретной опции инкапсулированной в отдельную функцию, а не общую спагетти-мешанину.
И в дальнейших раундах рефакторинга можно и весь последующий код сделать гораздо более удобоваримым.
Your hobby is extrapolating
Ну, видите, как интересно. Более реальный пример наглядно показал, что вы пока что просто плохо умеете программировать. Желаю вам в этом году повысить свой профессиональный уровень, а то подрываете мне тут имидж программистов на Elixir. Ая-яй-яй.
ТоталКоммандер вообще на Delphi, насколько я помню, также как и классический Skype
А идея оценивать удобство ЯП по скриншотам GUI звучит так, что вы знаете толк в извращениях. Тем более, что судя по вашим скриншотам, вы совсем какой-то древний UI-фреймворк используете. Даже 20 лет назад такой интерфейс выглядел устаревшим как какая-то программа времён Windows 95.
Комментарии не относятся к LOC, так же как и пустые строки.
Так вы сами не смогли придумать атомарное действие, нуждающееся в 50 строках. И зачем то привели пример из 2 строк. Максимально неконструктивный троллинг.
Это для кого это типовой пример? Для разработчиков Maple или Wolfram Mathematica?
Да и, судя по количеству переменных и параметров, придётся вам численные методы применять, а они отлично абстрагируются в отдельные функции.