Если в 90-е годы дети понимали в компьютерах лучше, чем многие учителя информатики, сейчас, они умеют нажимать две кнопки на планшете, не знают ни что такое файл, ни директория, ни оперативная память.
И этого достаточно. Компьютерами в 90е было просто невозможно нормально пользоваться, не зная этих вещей.
Есть мнение, что это антипаттерн и у функции должен быть единственный return. Но это обсуждаемо. Так или иначе, ранний return также ухудшает читаемость кода, заставляя прыгать взглядом по функции вверх — вниз.
Правило одного return пришло из Си подобных языков, где нужно было освобождать память и ресурсы при выходе из функции. Ранний return отсекает граничные случаи (например валидация), и не несёт особой смысловой нагрузки, т.е. не должен заставлять прыгать взглядом.
Незначительные неудобства, связанные с необходимостью вставки префикса FATAL перед окончательным сообщением журнала или записи непосредственно в os.Stderr с помощью fmt.Fprintf, не является достаточным основанием для расширения пакета матодом log.Fatal.L
И мы будем вручную формировать шаблон сообщения лога, вместо того, что бы это сделать через логгер? Крайне странная позиция.
Оригинальная статья 2015 года, с тех пор даже авторы go lang успели переобуться и изменить своё отношение под давлением общественности к некоторым аспектам языка.
В длительных операциях, например пакетная обработка N записей, которые ни в коем случае не должны прерываться, логично записать сообщение с error или waring уровнем для записи которую не удалось обработать и перейти к следующей.
Когда вся программа накрылась, код возврата говорит лишь о том, что случилась ошибка, причину ошибки логично поместить в лог с уровнем fatal.
Как правило нужны уровни логирования:
info\debug — информационный уровень, который никто не читает, пока не произойдёт ошибка.
waring\error — это уже сигнал для системы мониторинга, что в программе что то идёт не так и вероятно нужна реакция от человека. Но в целом программа продолжает работать.
fatal — это уже пожарная сигнализация, программа столкнулась с ошибкой, на которую не предусмотрена обработка.
Если правильно помню, более правильным способом анимации являлся программный вывод изображения прямо на canvas формы, потом следовало перезаливка всего canvas или прямоугольника спрайта цветом фона, и смена изображения кадра спрайта и его положения. Правда в таком случае получалось сильное мигание формы, так как canvas — медленный.
Ну и совсем просто игры и базовую анимацию можно делать в старом добром флэше, который успешно закапали в угоду деревянному js.
В целом это всё схожие паттерны.
Всё остальное в статье. Пришло это не из java, а из ruby (а туда надо полагать из функциональщины). Один сервис с десятью методами запросто превращается в God Object.
Вот с типизацией действительно есть некоторая проблема, дженерики тут немного мимо, InteractorResult — уже сам по себе контейнер, вместо него мог быть какой нибудь именованный tuple.
У Command немного другая сигнатура и назначение. «Команда» en.wikipedia.org/wiki/Command_pattern позволяет проводить отложенные операции (аргументы вызова передаются в конструктор) и производить их отмену.
Прошу прощения. Тогда ответ будет такой — смотря как мы определим эту самую злополучную «единственную ответственность», и что будет находиться внутри валидации. В любом случае, валидация — не обязательна и мы можем делегировать её другому классу (в том числе в другому интерактору).
Здесь же она скорей нужна для использования интеракторов в цепочке: result = doSomething(params) //вернул negative result
result = doSomething2(result) // проверили, что на вход пришёл negative result и вернули его же
«return early» относится больше к coding style и говорит лишь о том, что оператор return (с негативным результатом) должен находиться как можно раньше в теле функции.
Краткий пример (псевдокод):
if (valid($params)) {
doSomething()
} else {
...
}
//return early
if (!valid($params)) {
return
}
doSomething()
Пока что не совсем понятны преимущества такого подхода даже перед самым простым решением в лоб — запилить какой-то класс сервис, который будет заниматься созданием Книги, и дерганьем других сервисов, типа Email-sender`а если его нужно отправить Email при создании книги.
У такого сервиса будет хотя бы явно-определенный интерфейс с типизацией.
Проблема «сервисов» в том, что само их определение достаточно размытое и каждый может вкладывать в этот термин свой смысл. Интерактор (или же «операция») — по факту и есть максимально узко специализированный «класс-сервис», отвечающий за конкретную операцию бизнес-процесса. Интерфейс интерактора в данном случае — один метод call. Типизация (type hinting) к сожалению потерялась в угоду другим плюшкам. Это особенность конкретной реализации интерактора. Сам же трэйт тут нужен по двум причинам — что бы примеры кода в тексте были максимально приближены к исходному тексту, и для уменьшения количества шаблонного кода плюс единообразный интерфейс всех интеракторов.
Зона ответственности класса так же весьма размыта. Судя по коду из базового трейта Интерактора автор уже как минимум предлагает прикручивать к реализации валидацию входящего запроса.
Нет, валидация именно входящего запроса не предполагается. Валидация входных параметров может быть частью операции и реализует «return early» паттерн. Например, на вход может подаваться результат другого интерактора.
Думаю, чтобы пустить такое решение в продакшен должны быть действительно веские аргументы. Я не представляю как понять что он делает не запустив его, или не покопавшись в документации по магическим методам.
Это всего лишь реакция «триггер» на наличие магических методов и чего то нового. Если мы имеем некий AddBookService, это точно так же ничего не говорит нам о том, что он делает и как его запустить не покопавшись в документации (или исходном коде). Когда в тоже самое время реализация интеракторов рассмотренная в тексте, сводит интерфейс класса к одному единственному публичному методу (call\__invoke). Некая «магия» конечно присутствует, но без этого не обходится ни один современный фрэймворк, так как эта магия призвана упрощать нам жизнь.
Ну и тащить что то в продакшен тут не предлагается, цель текста — рассмотреть и обсудить паттерн, показать простейший пример разработки через тестирование.
Не совсем понял, как работает pipelining со стороны сервера. Выигрыш в производительности возникает на том, что к моменту вызова epoll на стороне сервера, уже скопилась пачка запросов и он их выгребаются скопом, а не по одному? Но ведь тоже самое должно случится и в том случае, если много клиентов пишут быстрее чем сервер обрабатывает одиночный запрос и накапливается очередь запросов.
И видимо редис тоже буферизует ответы и по возможности отправляет их пачкой?
Если хотелось просто переписать работающий продукт на другой ЯП ради переписывания — то всё ок. Из трёх пунктов мотивации на переписывание, только первый выглядит адекватным для перехода именно на go. Если важна была скорость работы продукта с минимум затрат на переписывание, то можно было взглянуть на crystal (или elixir) которые имеют ruby синтаксис и семантику. А менять ООП язык на «процедурный» фишка которого concurrency, и не имея в требованиях эту concurrency — звучит странно.
Формальное покрытие на уровне 90%-100%, фактическое будет ниже.
Я как раз и для военной и для космической делаю. Нет там такого.
Вы в одиночку поддерживаете какой то код для военки и космоса? Я могу судить про эту отрасль только из тех же публикаций на хабре, где не однократно отмечалось, что любое изменение кода — это множественные проверки и сертификации.
Если структура достаточно прозрачна и отработана, то управлять такой программой достаточно просто. А вот постоянно менять тесты на неё — не очень просто.
Есть отличный маркер плохо написанных тестов — когда одна строчка изменения в рабочем коде каскадно вызывает 100 (условно) правок в тестах. Хорошо изолированный код тестов менять не труднее, чем сам код, при этом есть гарантия, что у вас ничего не отвалится в других местах. Нужно точно также учиться грамотно писать тесты, как и учиться писать грамотный рабочий код. Пожалуй это вполне могут два разных независимых навыка которые прокачиваются с опытом.
На моей практике обычное соотношение строк кода к тестам это от 1:1 до 1:3. Базы данных — это отдельный случай, от которых требуется повышенная надёжность на уровне 146%. Тоже самое касается например и космической, авиационной, медицинской техники, где каждые 10 строчек кода по трудозатратам выливаются в эквивалент 100-1000 строк типичного веб приложения. Что касается игр, физика, графика — это движок, по отдельности они прекрасно тестируются я не поверю, что промышленные движки вроде Unreal Engine не используют тесты.
Если у вас есть программа на 68_000 строк кода, которая постоянно меняется и без какого либо покрытия тестами — это и есть прямой путь в психушку.
Тут есть хороший посыл, что использование фич из ФП не равно программированию в функциональном стиле, и что бы научится программировать в функциональном стиле, необходимо использовать функциональный ЯП.
И этого достаточно. Компьютерами в 90е было просто невозможно нормально пользоваться, не зная этих вещей.
Правило одного return пришло из Си подобных языков, где нужно было освобождать память и ресурсы при выходе из функции. Ранний return отсекает граничные случаи (например валидация), и не несёт особой смысловой нагрузки, т.е. не должен заставлять прыгать взглядом.
И мы будем вручную формировать шаблон сообщения лога, вместо того, что бы это сделать через логгер? Крайне странная позиция.
В длительных операциях, например пакетная обработка N записей, которые ни в коем случае не должны прерываться, логично записать сообщение с error или waring уровнем для записи которую не удалось обработать и перейти к следующей.
Когда вся программа накрылась, код возврата говорит лишь о том, что случилась ошибка, причину ошибки логично поместить в лог с уровнем fatal.
Как правило нужны уровни логирования:
Ну и совсем просто игры и базовую анимацию можно делать в старом добром флэше, который успешно закапали в угоду деревянному js.
Всё остальное в статье. Пришло это не из java, а из ruby (а туда надо полагать из функциональщины). Один сервис с десятью методами запросто превращается в God Object.
Вот с типизацией действительно есть некоторая проблема, дженерики тут немного мимо, InteractorResult — уже сам по себе контейнер, вместо него мог быть какой нибудь именованный tuple.
Здесь же она скорей нужна для использования интеракторов в цепочке:
result = doSomething(params) //вернул negative result
result = doSomething2(result) // проверили, что на вход пришёл negative result и вернули его же
Краткий пример (псевдокод):
Проблема «сервисов» в том, что само их определение достаточно размытое и каждый может вкладывать в этот термин свой смысл. Интерактор (или же «операция») — по факту и есть максимально узко специализированный «класс-сервис», отвечающий за конкретную операцию бизнес-процесса. Интерфейс интерактора в данном случае — один метод call. Типизация (type hinting) к сожалению потерялась в угоду другим плюшкам. Это особенность конкретной реализации интерактора. Сам же трэйт тут нужен по двум причинам — что бы примеры кода в тексте были максимально приближены к исходному тексту, и для уменьшения количества шаблонного кода плюс единообразный интерфейс всех интеракторов.
Нет, валидация именно входящего запроса не предполагается. Валидация входных параметров может быть частью операции и реализует «return early» паттерн. Например, на вход может подаваться результат другого интерактора.
Это всего лишь реакция «триггер» на наличие магических методов и чего то нового. Если мы имеем некий AddBookService, это точно так же ничего не говорит нам о том, что он делает и как его запустить не покопавшись в документации (или исходном коде). Когда в тоже самое время реализация интеракторов рассмотренная в тексте, сводит интерфейс класса к одному единственному публичному методу (call\__invoke). Некая «магия» конечно присутствует, но без этого не обходится ни один современный фрэймворк, так как эта магия призвана упрощать нам жизнь.
Ну и тащить что то в продакшен тут не предлагается, цель текста — рассмотреть и обсудить паттерн, показать простейший пример разработки через тестирование.
И видимо редис тоже буферизует ответы и по возможности отправляет их пачкой?
Формальное покрытие на уровне 90%-100%, фактическое будет ниже.
Вы в одиночку поддерживаете какой то код для военки и космоса? Я могу судить про эту отрасль только из тех же публикаций на хабре, где не однократно отмечалось, что любое изменение кода — это множественные проверки и сертификации.
Есть отличный маркер плохо написанных тестов — когда одна строчка изменения в рабочем коде каскадно вызывает 100 (условно) правок в тестах. Хорошо изолированный код тестов менять не труднее, чем сам код, при этом есть гарантия, что у вас ничего не отвалится в других местах. Нужно точно также учиться грамотно писать тесты, как и учиться писать грамотный рабочий код. Пожалуй это вполне могут два разных независимых навыка которые прокачиваются с опытом.
Если у вас есть программа на 68_000 строк кода, которая постоянно меняется и без какого либо покрытия тестами — это и есть прямой путь в психушку.
С каких пор Raspberry Pi стал микроконтроллером? Это же полновесный одноплатный компьютер.