На прошлой неделе я был на презентации. Моя команда и я рассказывали, как мы планируем реализовать веб-приложение для большой компании, которая недавно наняла нас. Во время той презентации мне пришлось продать их на наших «Методиках разработки программного обеспечения». Я проинформировал их исполнительную команду ранее, когда они выбрали Grability для ее создания, но теперь пришло время технической команде услышать это. Чтобы они услышали, как эта удивительная новая компания, которую они наняли, собиралась приступить к делу и на самом деле построить эту штуку.

Я рассказал о некоторых принципах, которые я и моя команда стараемся придерживаться, а затем коротко сказал:

Мы скорее дублируем код, чем неправильную абстракцию.

Как я и ожидал, они были где-то между шоком и ужасом.

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

дублирование кода - плохая практика, пожалуйста, не делайте этого здесь

Я попытаюсь объяснить, с моей точки зрения, почему дублирование кода - «плохая практика» и почему в некоторых сценариях это лучше.

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

В этом, конечно, есть смысл! Кто бы этого не хотел? Каким чудовищем вы должны быть, чтобы изменить место во многих местах, а не только в одном?

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

Но попросту говоря, вы избегаете кода, который трудно изменить. Дублированный код - это просто практика, которая в некоторых сценариях может затруднить изменение кода.

А теперь поговорим о том, как это сделать. Как избежать дублирования кода? Итак, вы создаете метод или класс, содержащий дублированный код, и вызываете его из любого необходимого места. Заметили вы это или нет, но чтобы избежать дублирования кода, вам нужно создать абстракцию.

Абстракция - это упрощенное представление сущности, в котором не учитываются неважные детали - Джон Остерхаут, Философия проектирования программного обеспечения.

Функции и классы - это абстракции. Они обеспечивают упрощенное представление: сигнатуру (-ы) метода (-ов) и скрывают неважные детали: сам код, создающий функциональность.

Большинство из нас обычно упускают из виду то, что у абстракций есть цена. Как и все в жизни, абстракции не бесплатны. Это не волшебный код, который неизменно улучшает вашу кодовую базу. Они могут пойти не так:

  • Они могут скрыть важные детали: например, вам может потребоваться заставить код вести себя по-разному в зависимости от того, где он вызывается, но как это сделать, не очевидно или просто невозможно из методов. подпись (и).
  • Они могут включать неважные детали в «упрощенное» представление: помните, как в Java, чтобы читать сериализованные объекты из файла, вы должны создать ObjectInputStream из BufferedInputStream из FileInputStream? И я нет. Теперь подумайте о том классе, которому требуется тонна истинных / ложных аргументов только для создания и повторного использования простой функциональности.
  • Они могут вводить в заблуждение: на основании сигнатуры метода (ов) абстракции могут заставить вас поверить в то, что код будет выполнять X. Когда на самом деле он выполняет XYZ. Только то, что основано на непонятном параметре, будет XZ или ABCX. У меня был опыт, когда метод getProducts в конечном итоге изменял внутреннее состояние, вызывая API-интерфейсы и вычислял несколько других неясных свойств, которые мне не нужны, о которых я не знал или не мог предположить, которые повлияют на то, что мне нужно делать.
  • Их может быть труднее понять. Смешайте все вышеперечисленное, дайте ему приправить несколько спринтов, и вы получите массу абстракций, которые приносят больше вреда, чем пользы, хотя они, вероятно, уменьшают дублирование кода.

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

Итак, когда абстракции идут не так? Когда вы их заставляете:

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

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

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

Итак, почему я предпочитаю дублированный код? это зависит от обстоятельств. Это зависит от того, что будет легче понять и изменить: несколько копий кода или абстракцию, которую мне придется создать.

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

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

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