Как стать автором
Обновить
890.81
OTUS
Цифровые навыки от ведущих экспертов

Почему в вашем коде так сложно разобраться

Уровень сложностиПростой
Время на прочтение7 мин
Количество просмотров14K
Автор оригинала: Stephen Young

“Так, что здесь, черт побери, происходит?!?”

Сейчас 1:30 ночи, и я смотрю на фрагмент кода, который написал около месяца назад. В то время он казался мне произведением искусства. Все здесь имело смысл. Он был элегантен, прост и замечателен. Но больше нет. У меня завтра дедлайн, а я обнаружил баг всего несколько часов назад. То, что казалось простым и логичным в то время, сейчас просто не поддается моему пониманию. Конечно, если я написал этот код, мне ведь должно хватить мозгов, чтобы понять его?

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

Проблема №1 – Слишком сложные ментальные модели

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

Чтобы решить любую реальную проблему, нам сначала нужно сформировать ее ментальную модель. Можно думать об этом как о идейном замысле, лежащем в основе вашей программы. Затем вам нужно сформировать модель решения, которая позволит достичь этого замысла. Давайте назовем это семантической моделью. Никогда не путайте замысел вашей программы с вашим решением для достижения этого замысла. Мы склонны рассуждать в первую очередь с точки зрения решения и часто пренебрегаем формированием модели самого замысла.

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

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

Проблема №2 – Плохой перевод семантических моделей в код

Как только вы сформировали наилучшую семантическую модель, пришло время превратить ее в код. Давайте назовем это синтаксической моделью. На этом этапе вы пытаетесь перевести суть вашей семантической модели в синтаксис понятный компьютеру.

Если ваша семантическая модель хороша, но вы сплоховали при переводе ее в код, то вам придется нелегко, когда вам нужно будет вернуться, чтобы изменить свой код на более позднем этапе. Если у вас в памяти еще свежа семантическая модель, сопоставить с ней свой код легко. Нетрудно вспомнить, что переменная под именем “x” представляет дату создания записи, а “y” — дату ее удаления. Когда вы вернетесь к этому коду через три месяца, у вас уже не будет этой семантической модели в голове, поэтому теперь те же самые имена переменных не вызывают ничего, кроме недоумения.

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

Итак, как вы можете это сделать?

Именование и структура классов

Если вы используете объектно-ориентированный язык, постарайтесь, чтобы структура и имена классов были как можно ближе к вашей семантической модели. Предметно-ориентированное проектирование (DDD) — это подход, который уделяет этой практике особое внимание. Даже если вы не согласны с подходом объектно-ориентированного проектирования во всей его полноте, вам все-равно следует очень тщательно продумывать структуру и имена классов. Каждый класс — это подсказка, которую вы оставляете как всем остальным, так и самому себе, и которая поможет вам восстановить свою ментальную модель, когда вы вернетесь позже.

Именование переменных, параметров и методов

Старайтесь избегать общих имен для переменных и методов. Не называйте метод “Process”, если больше смысла будет иметь “PaySalesCommision”. Не называйте переменную “x”, когда она может быть названа “currentContract”. Лучше не стоит называть параметр “input”, когда ему больше подходит “outstandingInvoices”.

Принцип единственной ответственности

Принцип единственной ответственности (SRP) является одним из основных принципов объектно-ориентированного проектирования (OOD) и связан с надлежащим именованием классов и переменных. Он гласит, что любой класс или метод должен служить одной и только одной задаче. Если вы хотите дать классам и методам осмысленные имена, они должны иметь одну четко определенную цель. Если один класс считывает и вносит записи в вашу базу данных, рассчитывает налог с продаж, уведомляет клиентов о продаже и формирует счет, вам вряд ли повезет в поиске хорошего имени для него. Я часто прибегаю к рефакторингу класса, которому не могу дать достаточно короткое имя, описывающее все, что он делает. Более подробное обсуждение принципа единственной ответственности и других принципов объектно-ориентированного программирования вы можете найти в моем посте про объектно-ориентированное проектирование.

Адекватные комментарии

Если вам нужно сделать что-то по причине, которая не ясно прослеживается в вашем коде, пожалейте себя в будущем и оставьте заметку, описывающую, почему вы должны были сделать именно так. Комментарии, как правило, быстро устаревают, поэтому я предпочитаю, чтобы код был как можно более самоописательным, а комментарии лишь объясняли, почему вам нужно было что-то сделать, а не то, как это было сделано.

Проблема № 3 – Недостаток группирования

Группирование (chunking) в психологии представляет ментальный мнемонический процесс, который включает разбивку массива на известные и неизвестные для человека фрагменты, и последующее объединение элементов каждого неизвестного фрагмента в единый комплекс, который для памяти становится одним целостным объектом. Итак, как это применимо к программированию? По мере того, как вы приобретаете опыт разработки, вы начинаете замечать повторяющиеся паттерны, которые снова и снова появляются в ваших решениях. Очень авторитетная книга “Паттерны объектно-ориентированного проектирования” (Design Patterns: Elements of Reusable Object-Oriented Software) стала первой книгой, в которой перечислены и объяснены некоторые из этих паттернов. Однако группирование применимо не только к паттернам проектирования и объектно-ориентированному программированию. В функциональном программировании (FP) есть ряд хорошо известных стандартных функций, которые служат той же цели. Алгоритмы — это еще одна форма группирования (подробнее об этом позже).

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

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

Проблема №4 – Не вполне ясное использование

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

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

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

Проблема №5 – Путь от модели к модели не очень хорошо прослеживается

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

Проблема №6 – Изобретение велосипедов

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

Заключение

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

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

Особая благодарность Нику Янгу и Ульви Гулиеву за их вклад в эту статью. Первоначально опубликовано на syoung.org 3 ноября 2014 г.


В завершение хочу пригласить вас на бесплатный урок, в рамках которого мы поговорим о JHipster, а точнее о том, почему это стало так "модно и молодёжно", затронем Rapid Application Development и рассмотрим некоторые примеры использования.

Теги:
Хабы:
Всего голосов 25: ↑23 и ↓2+21
Комментарии7

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS