Pull to refresh
7
0
Игорь @ZurgInq

Программист

Send message
Оригинальная статья 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 стал микроконтроллером? Это же полновесный одноплатный компьютер.
Абсолютно верное замечание. Такие ресурсоемкие не обязательные операции часто помещают под флаг, что бы они отключались на prod серверах. Как пример — php функция assert.
Максимальный наклон — 15%.
Препятствия до 1 см, щели и пробелы до 3 см

Не знаю как столицах, но в моём городе он далеко не уедет.
У каких людей и кто это «нас»? Тот же CaH4e3 упомянутый в тексте вроде как живёт в у «нас», т.е. в России.
Не совсем понятно какие ограничения накладывает использование RoadRunner. На вскидку, не должны использоваться глобальные переменные и объекты, включая разные singleton экземпляры. Как ведут себя в такой среде коннекты до БД, tcp?
Типичное веб приложение не генерирует десятки запросов в сеть или на диск в контексте пользовательского запроса. Если это интерактивная веб страничка (пусть даже очень нагруженный интернет магазин, блог или форум), там скорей всего не будет сетевых запросов во внешние сервисы или обращений в диск мимо БД. А бизнес логика как правило имеет синхронную природу. Асинхронность в node.js или golang даёт преимущество или при очень высоких нагрузках (много входящих запросов) или в специфичных случаях, когда само приложение генерирует много запросов\трафика.

С первыми двумя пунктами можно почти согласиться, но и тут легко найти объяснения:


  1. Если "а" — это локальная переменная, то это будет подсвечено редактором. И дополнительная фича — если вы перенесете вычисление этой переменной в функцию, то не нужно будет её переименовывать.
  2. Можно и должно выделяться пустой строкой перед возвращаемым результатом. И лучше придерживаться, что любая функция возвращает осмысленное значение (на то она и функция).
  3. А тут ruby-way, ООП и основная фишка ruby. В случае с a.positive — переменная "а" — может быть чем угодно, например специфичным value-object, а не только числовым значением.

Information

Rating
6,346-th
Location
Ижевск, Удмуртия, Россия
Date of birth
Registered
Activity