Pull to refresh

Преступный переинженеринг

Reading time 5 min
Views 18K
Original author: coderoom
Программистов часто обвиняют в том, что они делают свою работу неряшливо. В природе существует бесчисленное количество программ, которые падают, зависают или неожиданно пишут на экране «Превед медвед» миллион раз. Каков результат? Нечто вроде этого:
Чёртов компьютер и Excel испортили мне всю жизнь! Ненавижу свою жизнь.
мисс Alauren (как и любой другой в тот или иной момент своей жизни)


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

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

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

Ты делаешь это неправильно


Вы когда-нибудь видели, как кто-то применяет шаблон проектирования "Стратегия" там, где он вполне мог бы использовать простую конструкцию switch в 5 строк?

Существует миллион способов превратить простую конструкцию вроде этой:
switch(operation)<br>
{<br>
    case OP_ADD: return a + b;<br>
    case OP_SUBTRACT: return a - b;<br>
    case OP_MULTIPLY: return a * b;<br>

    default: throw new UnknownOperationException(operation, a, b);<br>
}

в отвратительное, неправильное чудовище-мутант, такое как этот, которое я не встроил в статью, потому что это оно чересчур длинное.

Наиболее коварной причиной переинжениринга является чрезмерное обобщение. Мы пере-обобщаем всё, что хоть капельку поддаётся обобщению. Пишем код для работы со списком студентов? Ну, мы могли бы работать с учителями, да и вообще с широкой общественностью. Когда-нибудь. Лучше добавить базовый класс Человек и его подкласс Студент. Или ещё можно создать класс Человек, от него наследовать класс ОбучающийсяЧеловек и от него Студент. Да, так гораздо лучше, не правда ли?

Так-то оно так, вот только теперь нам придётся поддерживать целых три класса, каждый со своими виртуальными методами и интерфейсами, возможно, разбитых на три разных файла, в то время как словаря (dictionary, map) в одну строчку было бы вполне достаточно.

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

Всё бы хорошо, но это не делает нас хорошими программистами. Такой переинжениринг не делает ничью жизнь лучше; он только делает наш код длиннее, труднее для понимания и изменения, и повышает вероятность содержания в коде ошибок. Мы только что сделали мир немного хуже. Это лежит где-то между бросанием бутылок посреди улицы и воровством автомобилей.

Дополнительные усилия, вызванные нашим переинженирингом, также несут здоровенные издержки:
  • Меньше времени остаётся на оттачивание пользовательского интерфейса;
  • Меньше времени остаётся на то, чтобы подумать о значимых последствиях фич, над которыми мы работаем;
  • Меньше времени остаётся на поиск ошибок, и (учитывая, что объём кода увеличился) больше времени потребуется на их исправление.


Да, переинженирингом класс Студент вы косвенно разрушили день мисс Alauren.

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

Don’t Worry, Be Happy


Я подозреваю, все лучшие программисты уже давно поняли это, но они не кричали об этом достаточно громко, чтобы все остальные могли услышать. Paul Graham совершенно прав, когда он предполагает, что краткость должна цениться:
Примем длину программы в качестве примерной оценки объёма работы, необходимого для её написания. не длину в символах, а длину в различных синтаксических элементах — по сути, высота дерева разбора. Это не совсем правда, что короткая программа требует меньше времени на написание, но это близко к правде. Взгляните на программу и спросите себя, существует ли способ написать её короче?
– Пол Грэм, Столетний язык («The Hundred Year Language»)


Вообще-то он говорил о дизайне языка; действительно, в статье "Краткость — сила" он чётко показывает, что можно написать и слишком короткую программу. Это потому, что в настоящий момент Пол Грэм скорее дизайнер языка, чем действующий программист. В противном случае он сказал бы:

Если вы собираетесь написать сто строк кода, чтобы решить задачу, которую можно было бы решить и десятью строками, остановитесь и спросите себя: какого чёрта?
– Марк, Преступный переинженеринг


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

В следующий раз, когда вы почувствуете побуждение написать хорошее, общее решение для простой задачи, остановитесь и спросите себя, что мешает вам написать простое, конкретное и короткое решение:
  1. Я беспокоюсь, что придется переписать его?
  2. Я беспокоюсь, что кто-то раскритикует его или, что я буду выглядеть глупо?
  3. Я беспокоюсь, что это недостаточно профессионально?

Если один из этих вариантов — ваш случай, то — расслабьтесь. Не переживайте. Ты беспокоишься, ты зовешь меня, я делаю тебя счастливым (строчка из песни Бобби Макферрина "Don't worry, be happy": «You worry, you call me, I make you happy.»).

А возьмите да напишите простое, конкретное, короткое решение и добавьте к нему короткий комментарий вроде этого: Заменить шаблоном Visitor, если этот код начнёт разрастаться.

Это — идеальное решение. В следующий раз, когда вы залезете в этот код, этот комментарий напомнит вам о том, что вы хотели сделать. Он покажет другим программистам, что вы рассматривали более «правильное» (сложное) решение, и в тот момент у вас была уважительная причина не начинать его делать пока что. С таким комментарием сложно спорить, ведь вы не обсуждали, что лучше: шаблон Стратегия или switch, вы всего лишь обсуждали, стоит ли использовать шаблон Стратегия для простой задачи из 3-4 случаев. В любом случае, это точно не та дискуссия, которая может показать вас с плохой стороны.

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

Ссылки по теме:
1. Stop Over-Engineering! (April 2002); a software-centric discussion from Software Development magazine.
2. Переинженеринг; статья на Wikipedia (англ.)
3. Краткость — сила (оригинал)

Tags:
Hubs:
+107
Comments 67
Comments Comments 67

Articles