Перевод статьи «When DRY Doesn’t Work, Go WET».
От редакции Techrocks. Мы выбрали эту статью для перевода, потому что затронутая тема достаточно интересна, в оригинале она привлекла внимание многих людей. Вместе с тем в разделе комментариев разгорелась жаркая дискуссия относительно советов из статьи. Мы хотели дать возможность русскоязычным читателям ознакомиться с точкой зрения автора статьи. Если вы опытный программист и у вас есть, что сказать по теме, — приглашаем поделиться в комментариях.
Примечание переводчика. В названии присутствует игра слов. Принцип DRY расшифровывается как «Don’t repeat yourself» и переводится как «не повторяйся». При этом само слово «dry» переводится как «сухой». Принципу DRY противопоставляется WET (расшифровка будет в статье). Само слово «wet» переводится как «мокрый». В общем, название можно перевести как «Если сухость не работает — намочите».
Я и сам совершал подобные ошибки, и многократно замечал их у других. Когда вы впервые читаете про принцип DRY, вы, скорее всего, понимаете его превратно.
В вашей голове складывается примерно такая картина:
Википедия: DRY предполагает, что вы не повторяете один и тот же код дважды.
Вы: Хмм, окей, я заменю все свои дубликаты абстракциями.
Это кажется удачным решением, но на самом деле им не является. Ваша абстракция зачастую ошибочна, и вот почему:
- Вы видите дублирование.
- Вычленяете дублирование в новую абстракцию (метод, класс).
- Заменяете дубликат новой абстракцией.
- Думаете, что ваш код идеален.
- Проходит время.
- У менеджера продукта появляются новые требования. Ваша абстракция практически полностью подходит для них.
- Вы начинаете реализовывать новые требования.
- Но есть одно маленькое «но»: ваша абстракция подходит практически полностью, но не абсолютно. Почему? Только 95% вашего кода, выделенного в абстракцию, соответствуют новым требованиям. И вместо того чтобы создать новую абстракцию из скопированных 95% кода уже существующей, вы решаете изменить код имеющейся у вас абстракции. Например, добавляете блоки
if...else
и передаете параметр, чтобы ваша абстракция могла осуществлять разные действия для разных решений. - Теперь ваша абстракция в разных случаях ведет себя по-разному.
- Поступает еще одно новое требование. Еще один дополнительный параметр. Еще одно условие. (Цикл повторяется, пока код не становится слишком сложным для чтения и поддержки).
- Поздравляю, вы создали неверную абстракцию.
Этот код больше не является простой общей абстракцией. Он превратился в нагруженную условиями процедуру. Его трудно понять, а вот сломать легко. Добавление новых фич стало невероятно сложной задачей, к тому же каждая новая фича еще больше усложняет код.
Это бесконечный цикл.
Что же делать?
Пишите все дважды.
WET
Концепция WET (англ. Write Everything Twice — «Пиши все дважды», или «We enjoy typing» — «Нам нравится печатать») является противоположностью DRY. Когда вы приступаете к созданию новой системы, вы не знаете, какими будут будущие требования. Поэтому не спешите с абстракциями.
Не забывайте: дублирование кода намного дешевле, чем плохая абстракция.
Допустим, вы создаете веб-приложение и на данный момент у вас нет на руках дизайна всех его страниц. Но в том, что вы уже видите, много кнопок, и на каждой странице они весьма похожи. Вы решаете выделить их в компонент под именем Button
и использовать на каждой странице. Пока все логично.
И тут поступает дизайн еще одной страницы. Вы просматриваете его и обнаруживаете внизу страницы необычную, причудливую кнопку.
Эта кнопка вроде бы выглядит так же, как и остальные, но имеет «изюминку». Для реализации этой изюминки вам нужно переписать 10% вашего компонента Button
, а также добавить условия и новые параметры.
Вы стоите перед дилеммой:
- Изменить
Button
, переписав 10% абстрагированного кода (добавить логические условия для поддержки логики новой кнопки). - Создать две абстракции, скопировав 90% кода из
Button
вFancyButton
.
Я знаю, что вы склоняетесь к первому варианту. Вы думаете, что у вас все получится и вы не создадите плохую абстракцию.
Но горькая правда в том, что таки создадите (если вы, конечно, не опытный программист, который точно знает, что и зачем он делает).
Скопируйте этот код. Не бойтесь.
Спустя некоторое время вы будете точно знать, как выглядят все ваши кнопки. Тогда вы сможете проанализировать текущую кодовую базу, найти дубликаты в кнопочных компонентах и вынести дублирование в хорошую абстракцию.
Если уже слишком поздно
Допустим, вы поняли, что уже слишком поздно что-то делать с неверной абстракцией. В таком случае лучше всего будет вернуться назад.
Сделайте следующее:
- Верните назад абстрагированный код.
- Удалите неиспользуемый параметр, который передавали в абстракцию для осуществления разных действий в разных сценариях.
- Удалите неиспользуемую логику.
Таким образом вы удалите абстракцию и условия для каждого вызова.
Напоследок…
Если вы понимаете, что передаете параметры и вставляете дополнительные условия в общий код, ваша абстракция ошибочна.
Дублирование кода лучше ошибочной абстракции, так что лучше продублировать.
[customscript]techrocks_custom_after_post_html[/customscript]
[customscript]techrocks_custom_script[/customscript]
«Спустя некоторое время вы будете точно знать, как выглядят все ваши кнопки.» это только в мире розовых единорогов в приложении больше никогда не будет новых кнопок.
«Тогда вы сможете проанализировать текущую кодовую базу, найти дубликаты в кнопочных компонентах и вынести дублирование в хорошую абстракцию.» и что мешает делать то же самое при каждом добавлении новой кнопки?
Вводить новые абстракции боязно:
а) когда понимаешь, что переделать их будет сложно (твою абстракцию моментально подхватят 5 человек в своих частях приложения)
б) когда не можешь найти выгоднейшую абстракцию на основе имеющейся информации (какие параметры кнопок стоит абстрагировать в данном проекте? цвет? размер? позиционирование? шрифт?)
В целом поддерживаю предложенное в статье решение — иногда проще отложить принятие решения до поступления новых данных. Считаю такой подход особенно эффективным на старте проекта. Даже в теории принятия решений есть стратегия «ничего не предпринимать» и она может оказаться самой выгодной на текущий момент.
Благодарю за статью! Практика показала что такой подход имеет место быть, и действительно абстракция выводится гораздо лучше на основе дубликатов.