Быть умным - это хорошо для разработчика. Изобретательность позволяет нам писать программное обеспечение, которое решает сложные проблемы реального мира. Однако умный код - не всегда хорошо. Во многих случаях - осмелюсь сказать, в большинстве - это очень плохо. Я сознательно стараюсь избегать написания кода, который может показаться умным. Умнее всего постараться не быть умным (да, очень 1984).

Разработчики склонны считать себя (довольно снисходительно) умными людьми. Не многие люди понимают, что мы делаем, и общество видит в разработчиках своего рода современного волшебника, пишущего нечитаемые магические заклинания в маленькой металлической коробке. В действительности, однако, мы не наполовину так умны, как думаем: например, если вы разработчик, вы наверняка знакомы с разочарованием от попыток понять какой-то загадочный фрагмент кода, который казался совершенно разумным и простым, когда вы его писали. пару месяцев назад.

Само собой разумеется, что любому программисту придется сталкиваться с разочарованием от бесчисленных попыток понять сложный код на протяжении своей карьеры. Конечно, есть настоящие причины писать сложный код: иногда есть строгие аппаратные ограничения, как, например, в первые годы существования электронных компьютеров, а иногда сама область проблемы по своей сути сложна. Однако, если вы читаете это, вы, вероятно, не живете в 1940-х и работаете над водородной бомбой, и более вероятно, что вы работаете над каким-то веб-приложением, использующим язык динамического программирования и полезный фреймворк. , так что вы, вероятно, можете воспользоваться этим и упростить задачу.

Почему же тогда мы настаиваем на написании излишне сложного и загадочного кода, когда нам это совершенно не нужно? Оказывается, причин много (хотя очень немногие из них хороши): произвести впечатление на начальника и коллег, почувствовать себя умным или гордиться собой, бросить вызов себе или просто от скуки. Все это вполне реальные причины, по которым люди сознательно пишут сложный код. Но люди также пишут сложный код непреднамеренно, хотя на самом деле изо всех сил стараются сделать обратное: вот что происходит, когда программист неверно истолковывает подлинные рекомендации по программированию и передовой опыт.

Хорошим примером этого является рекомендация Не повторяйся (DRY), которую повторяют как мантру на некоторых курсах информатики и в индустрии. Как люди, мы обладаем даром и склонностью распознавать закономерности - это то, что позволяет нам узнавать знакомые лица, ценить музыку и понимать языки, и это лишь несколько примеров. Мы также распознаем шаблоны в исходном коде, рефакторинг которого мы проводим по принципу DRY.

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

Исходное определение DRY из книги Прагматичный программист Ханта и Томаса гласит:

«Каждая часть знания должна иметь единственное, недвусмысленное и авторитетное представление в системе».

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

Давайте используем пример, чтобы проиллюстрировать неправильное использование DRY. Предположим, вы работаете над программным обеспечением для автосалона. Дилерский центр продает и обслуживает одну модель автомобиля, предлагая 3 плановых ТО через 10, 30 и 50 тысяч миль:

На первый взгляд, у вас может возникнуть соблазн ОСУШИТЬ код, извлекая три метода, которые вызываются при всех техобслуживании: check_break_fluid, check_battery_terminals и check_engine_oil. Полученный код более лаконичен:

DRYing создает этот новый метод basic_main maintenance. Это не очень наглядно: в то время как maintenance_* методы точно передают то, что от них ожидается (т. Е. «Выполнить обслуживание 10, 30 или 50 тысяч миль»), basic_maintenance - это своего рода произвольное имя, которое мы придумали, которое может означать что угодно. Это абстрактное творение, которое существует только для нашего удобства и ничего не представляет в реальном мире.

Давайте представим очень простое изменение требований: предположим, что нам больше не нужно проверять тормозную жидкость при проверке на 10 тысяч миль. Теперь мы должны решить между удалением check_break_fluid из basic_maintenance и добавлением проверки только для обслуживания 30k и 50k, тем самым снижая эффективность basic_maintenance в предотвращении повторения, или полностью исключить метод и вернуться к тому, как было в Примере №1.

Хотя в примере №1 больше повторов, чем в примере №2, он, возможно, более читабелен и нагляден. Кроме того, вероятность поломки снижается, если в требованиях будут внесены изменения, как мы только что описали. Имейте в виду, что это очень простой пример: все методы делают, это вызывают другие методы, которые не принимают никаких параметров; нет передачи аргументов, изменений состояния, преобразований и т. д. Более сложный пример еще больше повысил бы абстрактность и сложность СУШКИ кода.

Небольшое повторение предпочтительнее кода, который был СУХОЙ неправильно или чрезмерно. Если абстракцию, полученную в результате рефакторинга DRY, труднее понять, чем альтернативу (прохождение нескольких повторяющихся участков кода), тогда программист, вероятно, страдал апофенией, видя несуществующие шаблоны кода - и, следовательно, неправильно применял DRY. Санди Мец очень четко резюмирует это в своем выступлении на RailsConf в 2014 году:

«Предпочитайте дублирование неправильной абстракции».

С учетом сказанного, есть еще одна аббревиатура, дополняющая DRY: DAMP. DAMP означает описательные и содержательные фразы. Хотя в основном он направлен на тестирование, общий принцип признания ценности информативности применим ко всем видам кода : хороший код не слишком повторяющийся, но и не слишком абстрактный и общий. Иногда нет общего случая, который можно было бы абстрагировать, есть всего пара конкретных, конкретных случаев, которые вы должны рассматривать как конкретные, особые случаи.

Цель DRY, DAMP и всех других причудливых принципов программирования - помочь нам создать лучший код. Если результатом СУШКИ чего-либо является более сложный и менее обслуживаемый код, то мы потеряли цель СУХОЙ. Принципы программирования не являются законами природы, которые гарантируют лучший код, а это означает, что они не могут применяться повсеместно. Больше, чем знание того, как грамотно рефакторинг и СУШИТЬ код, важно знать, когда что-то нужно СУШИТЬ, а когда оставить в покое.