Pull to refresh
0
Игорь @ZurgInqread⁠-⁠only

Программист

Send message
Если в 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 — звучит странно.
Это при каком покрытии? 100%?

Формальное покрытие на уровне 90%-100%, фактическое будет ниже.

Я как раз и для военной и для космической делаю. Нет там такого.

Вы в одиночку поддерживаете какой то код для военки и космоса? Я могу судить про эту отрасль только из тех же публикаций на хабре, где не однократно отмечалось, что любое изменение кода — это множественные проверки и сертификации.

Если структура достаточно прозрачна и отработана, то управлять такой программой достаточно просто. А вот постоянно менять тесты на неё — не очень просто.

Есть отличный маркер плохо написанных тестов — когда одна строчка изменения в рабочем коде каскадно вызывает 100 (условно) правок в тестах. Хорошо изолированный код тестов менять не труднее, чем сам код, при этом есть гарантия, что у вас ничего не отвалится в других местах. Нужно точно также учиться грамотно писать тесты, как и учиться писать грамотный рабочий код. Пожалуй это вполне могут два разных независимых навыка которые прокачиваются с опытом.
На моей практике обычное соотношение строк кода к тестам это от 1:1 до 1:3. Базы данных — это отдельный случай, от которых требуется повышенная надёжность на уровне 146%. Тоже самое касается например и космической, авиационной, медицинской техники, где каждые 10 строчек кода по трудозатратам выливаются в эквивалент 100-1000 строк типичного веб приложения. Что касается игр, физика, графика — это движок, по отдельности они прекрасно тестируются я не поверю, что промышленные движки вроде Unreal Engine не используют тесты.

Если у вас есть программа на 68_000 строк кода, которая постоянно меняется и без какого либо покрытия тестами — это и есть прямой путь в психушку.
Тут есть хороший посыл, что использование фич из ФП не равно программированию в функциональном стиле, и что бы научится программировать в функциональном стиле, необходимо использовать функциональный ЯП.
Внутри поместился микроконтроллер Raspberry Pi

С каких пор Raspberry Pi стал микроконтроллером? Это же полновесный одноплатный компьютер.

Information

Rating
Does not participate
Location
Ижевск, Удмуртия, Россия
Date of birth
Registered
Activity