Что такое спагетти-код и как его исправить
Что такое спагетти-код
Спагетти-код — это сложный и запутанный код, который состоит из множества одинаковых кусков. Выглядят они так, словно их копировали с помощью Ctrl+C и Сtrl+V, но разбавляли разными цифрами и данными.
Вот пример такого спагетти-кода:
wait_nomsg:
if ((inb(tmport) & 0x04) != 0) {
goto wait_nomsg;
}
outb(1, 0x80);
udelay(100);
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x80) != 0) { /* bsy ? */
goto wait_io;
}
}
goto TCM_SYNC;
wait_io:
for (n = 0; n < 0x30000; n++) {
if ((inb(tmport) & 0x81) == 0x0081) {
goto wait_io1;
}
}
goto TCM_SYNC;
wait_io1:
inb(0x80);
val |= 0x8003; /* io,cd,db7 */
outw(val, tmport);
inb(0x80);
val &= 0x00bf; /* no sel */
outw(val, tmport);
outb(2, 0x80);
TCM_SYNC:
/* ... */
small_id:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto small_id;
}
G2Q5: /* srch from max acceptable ID# */
k = i; /* max acceptable ID# */
G2Q_LP:
m = 1;
m <<= k;
if ((m & assignid_map) == 0) {
goto G2Q_QUIN;
}
if (k > 0) {
k--;
goto G2Q_LP;
}
G2Q_QUIN: /* k=binID#, */Больше примеров спагетти-кода можно посмотреть здесь.
В таком коде куски дублируют друг друга и последовательность его выполнения неочевидна. В спагетти-коде сложно найти ответы даже на самые простые вопросы. Например, где реализуется эта функциональность, где создается экземпляр объекта, когда и какое вложенное условие сработает и какой кусок нужно исправить, чтобы починить программу. Файл с кодом похож на тарелку с пастой: все запутано настолько, что непонятно, где начало и конец у каждой макаронины.
Спагетти-код — большая проблема для любого разработчика. С программой, состоящей из спагетти-кода, трудно работать: в нее нельзя добавлять новые функции и исправлять элементы. В ней постоянно что-то ломается, но чинить баги сложно, так как приходится следовать за каждым элементом с начала и до конца.
Сегодня термин «спагетти-код» используют применительно к любому плохому коду. Но на самом деле не каждый плохой код — спагетти. Исторически этот термин использовали в ситуации, когда разработчик создавал запутанную, сложную для запоминания структуру за счет избыточного оператора goto.
Что такое оператор goto
Оператор — это команда, которая позволяет перейти к определенной части программы. С помощью goto можно определить любую последовательность выполнения кода.
Если представить день из жизни как программу, то, предположим, последовательность действий будет такая: выпить кофе, проехать в метро, зайти в офис. Оператор goto позволит изменить эту последовательность в любом направлении: скажем, сначала мы выпьем кофе, потом зайдем в офис, а затем проедем в метро. Или наоборот: метро, кофе — а далее окажемся в той части дня, где мы на дне рождения у троюродной тети.
Goto есть во многих языках программирования. Но в высокоуровневых языках — Python, C# — использование этого оператора ограничено. Среди разработчиков применение оператора и вовсе считается плохим тоном. Goto может основательно испортить код: сделать его беспорядочным и запутать ход выполнения программы.
Почему так происходит:
- Оператор можно ставить абсолютно в любом месте кода, из-за чего вместо последовательного логичного написания кода получаются хаотичные прыжки от одного куска к другому.
- Оператор может группировать исходный код и тем самым ломать его структуру. Это значит, что работать с кодом, как с конструктором Лего, переставляя блоки, уже не получится — каждое действие может просто сломать программу.
Это не значит, что goto сегодня совсем не используется. Его применяют — но с осторожностью и в случае острой необходимости, когда другие методы не работают.
Если игнорировать это правило и ставить goto где попало, код становится сплошной неожиданностью. Любая строчка кода может быть связана с другой. Возникает эффект спагетти: все части настолько тесно переплетены друг с другом, что фактически становятся единым целым.
Откуда берется спагетти-код
Goto не используется повсеместно примерно с 70-х годов XX века — именно тогда вышла статья нидерландского ученого Эдсгера Дейкстры «Доводы против оператора GOTO», которая стала «началом конца» для оператора. Но спагетти-код никуда не исчез и по-прежнему остается актуальной проблемой.
Есть несколько причин, почему программисты невольно продолжают писать спагетти-код:
- Нужно быстро написать код и протестировать, работает он или нет. Скопировать готовые куски кода проще, чем тратить время на выстраивание красивой логики. Поэтому программисты пишут спагетти-код, чтобы потом, когда будет время, исправить. Времени, конечно, всегда не хватает.
- Ход программы не продуман заранее. Если логики нет, ее пытаются настроить в процессе с помощью того самого goto или множества вложенных условных операторов (if). Правда, потом разработчики сталкиваются с тем, что добавить новую функциональность или что-то переписать будет уже невозможно.
- Программу пишут разные команды разработчиков. У каждой из них нет времени и ресурсов распутывать клубок, созданный предыдущими программистами: проще использовать те же самые методы и сказать, что это «они первые начали».
- У разработчика мало опыта. Он даже не догадывается, что что-то делает неправильно и не трогает код, который работает.
Как избавиться от спагетти-кода
Если в работе вам встретился спагетти-код, не опускайте руки — его можно исправить. Вот что предстоит сделать:
- Разобраться с функциональностью проекта: проверить, какие участки кода за что отвечают.
- Написать тесты, и, если возможно, — высокоуровневые, которые проверяют основные сценарии.
- Начать поэтапный рефакторинг — процесс улучшения кода, то самое «распутывание спагетти».
- Избавиться от оператора goto, если он есть.
- Структурировать код по модулям. У каждого из них должна быть своя цель, не зависящая от остальной логики кода — только так удастся избавить от запутывающих код зависимостей.
- На важные или сложные части кода написать модульные тесты — они помогут защитить продукт от внезапных поломок, когда из-за рефакторинга вдруг перестанет работать вся система.
Распутать спагетти-код непросто: это долгий и сложный процесс, в который будет вовлечена вся команда. Неважно, кто вы — начинающий или опытный разработчик. Главным правилом должен стать чистый код, который важно сразу писать хорошо. Или хотя бы не забывать перерабатывать его, если сначала пришлось написать черновик.
Как избежать спагетти в коде
- Напишите спецификацию или ТЗ к коду. Продумайте основные сценарии использования программы и распишите, какие части кода за что будут отвечать. Изучите логику проекта, посмотрите, какие методы и стили уже используются или будут использоваться в нем. Это поможет лучше понять структуру кодовой базы и начать сразу писать красивый чистый код.
- Пишите тесты до создания самого кода. Тогда вы начнете задумываться об интерфейсах программы, и дальше будет легче рефакторить код. Для сложных частей программы можно создать модульные тесты — они изолируют кусок кода и проверяют корректность его работы на промежуточном этапе, задолго до финального релиза. Чем больше таких тестов, тем выше вероятность того, что удастся очистить код от лишних элементов.
- Изучите, как пишут код другие разработчики. Посмотрите на код в опенсорс-проектах, почитайте про нейминг. Изучите, как другие разработчики борются с побочными эффектами.
- Проводите код-ревью. Это поможет обнаружить признаки спагетти-кода до того, как программа уйдет в релиз. Если же вы работаете в одиночку, то просто старайтесь не усложнять код — в любом языке программирования сейчас доступны фреймворки и библиотеки, которые помогут реализовать сотни функций парой строк.
- Если в код пришлось добавить комментарии, то пишите их правильно. Так всем членам команды — этой и последующих — будет гораздо проще разобраться, за что отвечает конкретный кусок кода.
- Больше практикуйтесь. Во время работы с кодом чаще задавайте себе вопрос: «А не сделал ли я спагетти?». Также читайте статьи на Хекслете под названием «Совершенный код» и смотрите видеолекции Кирилла Мокевнина.
