Найти в Дзене
ZDG

Работа с критикой, часть 2: Какие намерения куда ведут

Первая часть: Рассмотрим такую ситуацию: вы хотите сварить кашу. Нужно сходить в магазин за крупой. Тогда вы можете разбить процесс на составные части так: сначала у вас есть намерение купить крупу, затем у вас есть намерение сварить кашу. Когда вы что-то делаете, вы находитесь в процессе выполнения своего намерения. Ожидая зелёного сигнала светофора, вы осознаёте, что в этот момент вы исполняете своё намерение дойти до магазина. Сейчас ваши намерения синхронизированы. Чтобы осуществить намерение сварить кашу, надо сначала осуществить намерение сходить в магазин. Можно поступить и по-другому. Так как вы всё равно будете варить кашу, можно поставить кастрюлю с водой на огонь и уйти в магазин. Пока вы будете покупать крупу, вода закипит, и каша приготовится быстрее. Теперь, стоя у светофора, вы осуществляете два намерения параллельно: вы уже и в процессе варки каши, и в процессе похода в магазин. В случае синхронного осуществления намерений вы могли бы задержаться в магазине на несколько

Первая часть:

Рассмотрим такую ситуацию: вы хотите сварить кашу. Нужно сходить в магазин за крупой.

Тогда вы можете разбить процесс на составные части так: сначала у вас есть намерение купить крупу, затем у вас есть намерение сварить кашу.

Когда вы что-то делаете, вы находитесь в процессе выполнения своего намерения. Ожидая зелёного сигнала светофора, вы осознаёте, что в этот момент вы исполняете своё намерение дойти до магазина.

Сейчас ваши намерения синхронизированы. Чтобы осуществить намерение сварить кашу, надо сначала осуществить намерение сходить в магазин.

Можно поступить и по-другому. Так как вы всё равно будете варить кашу, можно поставить кастрюлю с водой на огонь и уйти в магазин. Пока вы будете покупать крупу, вода закипит, и каша приготовится быстрее.

Теперь, стоя у светофора, вы осуществляете два намерения параллельно: вы уже и в процессе варки каши, и в процессе похода в магазин.

В случае синхронного осуществления намерений вы могли бы задержаться в магазине на несколько часов, спасти котёнка и тому подобное. Потом вы бы вернулись и сварили кашу.

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

Очевидно, чем больше намерений вы хотите осуществить за один раз, в целях оптимизации своего времени и усилий, тем больше вероятность, что что-то пойдёт не так, и тем труднее становится за всем этим следить. Появляются зависимости одних событий от других. Давайте запомним это, чтобы вспомнить позже.

Намерения в программировании

Читая код, очень важно понимать – какое намерение здесь осуществляется? Понимание намерения гораздо важнее, чем синтаксис кода.

Вот пример одного и того же кода, но с разными комментариями:

  • a += 5; // прибавить 5 к переменной a
  • a += 5; // пополнить счёт на 5 рублей

В общем-то "прибавить 5 к переменной а" это тоже намерение, но оно формальное, оно не отражает сути того, что мы хотим сделать, и его описание не несёт никакой пользы.

Хорошо, когда истинное, а не формальное намерение видно просто по коду, без необходимости в комментариях.

А что, если в коде, как и в жизни, реализуются сразу несколько параллельных намерений? Опять же в целях оптимизации: раз уж мы в этом месте что-то делаем, то можно сделать и что-то ещё.

Никаких чудес не случится. Такой код закономерно окажется сложнее в понимании, и вариантов что-то в нём сломать будет больше.

Перейдём теперь к коду из игры "5 букв", который вызвал критику. Я убрал оттуда лишние детали и добавил комментарии.

Я также обозначил два намерения, которые осуществляются в коде. Это опрос игрока, а затем проверка результата. Как можно видеть, они осуществляются синхронно. Сначала одно, потом другое.

Это позволяет рассматривать их как два самостоятельных блока. Например, можно каждый из них заключить в функцию:

-2

И каждую функцию отдать на реализацию своему программисту. Программисты будут работать, не завися друг от друга, и будучи связаны только типами передаваемых и возвращаемых параметров.

Но можно заметить, что проверка "если слово отгадано правильно" присутствует в обоих намерениях. В первом, чтобы прекратить цикл опроса, а во втором, чтобы обработать результат.

Это одна и та же проверка, но используется она для разных намерений. Это не нравится комментатору, и он пишет следующее:

-3

Хм... Давайте зафиксируем:

  • учим плохому
  • двойная проверка ok_cnt это неэффективно
  • нам предлагается вывод сообщения о победе внести внутрь цикла, это будет, соответственно, эффективно

Сделаю как сказано:

-4

Теперь я обрабатываю победу игрока прямо там, где происходит выход из цикла. Но... вторая проверка никуда не делась, потому что кроме победы надо обработать и проигрыш тоже. То есть эффективность кода не повысилась, а намерение 2 теперь просочилось в намерение 1. Мы имеем параллельную обработку намерений. Я теперь не могу разбить их на две функции и каждую функцию отдать своему программисту.

Но у комментатора есть ещё замечание:

-5

Оказывается, оператор break, который специально придуман, чтобы быть внутри for, внутри этого самого for смотрится коряво. Почему? Какие-то личные заморочки или навязанное мнение от какого-то ютуб-гуру. Конечно, он не смотрится коряво, с чего ему вообще смотреться коряво? Но ладно, мне не сложно поменять:

-6

Итак, теперь нет корявого break, но зато появилась корявая (я так решил, я ведь так тоже могу) проверочная часть цикла for:

try_cnt < 7 && ws.ok_cnt < LETTER_CNT

В остальном ничего не изменилось – все проверки по-прежнему на месте. А бонусом внимательные читатели заметят, что их стало даже на одну больше, чем было.

Но у комментатора есть решение:

-7

Охохох... давайте писать реализацию.

Вот функции:

-8

Вот как изменился сам цикл:

-9

Сделано всё по рекомендациям!

Давайте посчитаем, сколько теперь нужно проверять условий:

  1. Функция check_guessed() проверяет одно условие, а цикл проверяет результат, который вернула функция – это 2 условия
  2. Функция check_tries() проверяет 1 условие, а цикл проверяет результат, который вернула функция – это ещё 2 условия

Итого 4 условия. Их стало меньше? Нет! Их стало больше в два раза в каждом шаге цикла! Не говоря уж о том, что появились два вызова функций. Эффективность кода просто зашкаливает.

Но это ещё не всё. А что там с нашими намерениями, с нашими бедными несчастными намерениями?

Как я вообще собираюсь кому-то объяснять, что обработка результата делается в двух разных функциях, которые вообще используются для организации цикла? Что это за адская дичь, господи?

Но у комментатора есть ещё идеи:

-10

Что ж, сгорел сарай, гори и хата. Сделаем:

-11

И цикл с дальнейшей проверкой:

-12

Все условия остались как и были – то есть цель повышения эффективности путём сокращения условий не достигнута. Появился дополнительный массив строк и индекс, появилась передача в функцию параметра для изменения по указателю (морщится).

Хорошего тут ровно одно: обратно появилось хоть какое-то подобие намерения 2. Однако оно уже не выглядит как "обработка результата". Оно выглядит как "вывод строки из массива по индексу". То есть только это, и всё. Это не то намерение, которое было изначально.

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

Но у комментатора есть ещё одна идея:

Это нам, значит, для самостоятельного изучения :)))

То есть, как строки хранятся в массиве и выбираются по индексу, который неявным образом меняется по указателю в какой-то функции, так же предлагается хранить указатели на функции и выбирать их по индексу. Одна из функций будет обрабатывать победу, другая проигрыш.

Всё это из-за одного (одного, единичного, не в цикле) якобы лишнего условия.

-14

С вашего позволения, я не буду это делать.