Кодовая связь введена DRY и OOD

14

Я ищу руководство по соединению DRY vs Code. Я не люблю дублировать мой код, а также мне не нравится связывание кода между несвязанными модулями. Поэтому я реорганизую дублирующий код, если найду идентично дублирующий код через год после того, как дублирование было введено. Однако я все чаще сталкиваюсь с ситуациями, когда реальный мир становится гораздо более непредсказуемым, и после рефакторинга кода возникают ситуации, которые требуют повторного раскладывания кода.

Например, если бы у меня был код для работы с бензиновыми автомобилями, бензиновыми внедорожниками, электромобилями и электрическими внедорожниками, допустим, я преобразовал дублирующий код в иерархию «бензин» и «электрическую», обе из иерархии «транспортных средств». Все идет нормально. И затем, моя компания представляет гибридный автомобиль и гибридный Semi, что потребует внесения основных изменений в мою первоначальную иерархию. Возможно, для этого потребуется «состав» между бензином и электрической иерархией.

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

Как найти оптимальный баланс между СУХОЙ и нежелательной связью кода?

user2549686
источник
1
Всегда хорошо не слепо следовать правилам, а сначала задействовать мозг.
gnasher729
достаточно справедливо - отсюда и руководящие принципы, а не правила.
user2549686
Вы читали вики-страницу на СУХОЙ (или ОАОО, как она раньше называлась)? wiki.c2.com/?OnceAndOnlyOnce Вот где много первоначального обсуждения этого произошло. (На самом деле, Уорд изобрел вики специально для обсуждения моделей и практик.)
Йорг Миттаг
Очень похожие ответы, даже если вопрос не совсем похож на дубликат: softwareengineering.stackexchange.com/questions/300043/…
Халк,

Ответы:

8

допустим, я рефакторил дублирующий код в иерархию «бензин» и «электрическую» иерархию, обе нисходящие из иерархии «транспортных средств». Все идет нормально.

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

Я думаю, что это одна из главных причин, почему люди переходят к композиции, а не к наследованию.

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

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

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

Является ли полученный дизайн ООП открыт для обсуждения

Ewan
источник
1
+1 за указание на то, что наследование - это проблема, а не СУХОЙ. Самый простой способ повторно использовать код - это удалить ненужные зависимости, и слишком часто люди упускают тот факт, что родительский класс (и его потомки) являются всеми зависимостями при использовании наследования. Только когда вы уменьшаете или устраняете зависимости, вы фактически получаете повторно используемый код.
Грег Бургхардт
3
« Является ли получающийся дизайн ООП открытым для обсуждения ». Все открыто для обсуждения. Вопрос, который нужно задать, - открыт ли он для разумных, рациональных дебатов? Ответ - нет. Я думаю, что ваш последний абзац умаляет другой очень хороший ответ.
Дэвид Арно
@ davidarno действительно не следите за вами, я прочитал OOD в заголовке как объектно-ориентированный дизайн? так что я отвечаю на вопрос, не пробираясь сквозь дебаты
Ewan
1
@Ewan: я здесь с Дэвидом Арно. Я думаю, что уже более двух десятилетий известно, что убеждение «если нет наследства, то это не ООП» - заблуждение.
Док Браун
Боже, это именно то обсуждение, которого я пытался избежать. есть мнения с обеих сторон, нет однозначного ответа
Ewan
3

Вы правы, следуя принципу СУХОЙ, вы можете увеличить связь между в противном случае не связанными модулями. Особенно в больших программных системах это может привести к ситуациям, когда не следование DRY может быть лучшей альтернативой.

К сожалению, ваш пример не очень подходит для демонстрации этого - описанные проблемы вызваны классическими ошибками при неправильном неоптимальном использовании наследования. Тем не менее, для того, что я написал выше, не имеет значения, будете ли вы преобразовывать общий код в общий базовый класс или в вспомогательный класс (состав). В обоих случаях можно заставить общий код поместить в библиотеку L и сослаться на эту библиотеку из двух ранее не связанных программ A и B.

Давайте предположим, что A и B были совершенно не связаны ранее, они могут быть версионированы, выпущены и развернуты независимо. Однако, помещая общий код в разделяемую библиотеку L, новые требования для A могут вызвать изменения в L, что теперь может вызвать изменения в B. Таким образом, это вызывает необходимость в дополнительных тестах и, возможно, новом цикле выпуска и развертывания для B.

Итак, как вы можете справиться с этой ситуацией, если вы не готовы отказаться от принципа СУХОЙ? Ну, есть несколько известных тактик, чтобы подойти к этому:

  1. Либо держите A, B и L как часть одного и того же продукта с одним общим номером версии, общим процессом сборки, выпуска и развертывания с высокой степенью автоматизации

  2. или сделать L продуктом самостоятельно, с младшими номерами версий (без несовместимых изменений) и номерами основных версий (возможно, содержащих критические изменения), и пусть A и B позволяют каждому ссылаться на разные строки версии L.

  3. Сделайте L как можно более твердым, и позаботьтесь о обратной совместимости. Чем больше модулей в L может быть повторно использовано без модификации (OCP), тем менее вероятны критические изменения. И другие принципы «SOLID» помогают поддерживать эту цель.

  4. Используйте автоматические тесты, особенно для L, но также для A и B.

  5. Будьте осторожны с тем, что вы вкладываете в L. Бизнес-логика, которая должна существовать только в одном месте системы, является хорошим кандидатом. Вещи, которые просто «выглядят одинаково» и могут измениться в будущем, являются плохими кандидатами.

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

Так что, в конце концов, это компромисс. Если вы хотите следовать принципу СУХОЙ в больших системах, вам нужно приложить гораздо больше усилий для создания надежных, повторно используемых компонентов - как правило, больше, чем вы ожидаете. Вы должны доверять своему суждению, когда оно того стоит, а когда нет.

Док Браун
источник
1

СУХОЙ - это еще одно структурное правило. Это означает, что если вы доведите это до крайности, это так же плохо, как если бы вы игнорировали это. Вот метафора, чтобы вывести вас из структурного мышления.

Люблю твоих близнецов. Убей клонов.

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

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

Вы можете сканировать их ДНК (код) все, что вам нравится. Клонов и близнецов трудно отличить друг от друга, если вы только смотрите на них. Вам нужно понять контекст, в котором они живут. Почему они родились. Какова будет их окончательная судьба.

Допустим, вы работаете в компании ABC. Он управляется тремя руководителями компаний, A, B и C. Все они хотят, чтобы вы создали программный продукт, но у каждого из них есть свои отделы. Все они хотят, чтобы вы начали с малого. Просто выведите простое сообщение на данный момент. Итак, вы пишете «Hello World».

Ненавидит, хочет, чтобы вы указали название компании.

B любит это, хочет, чтобы вы оставили это в покое и добавили название компании на заставку.

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

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

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

A, B и C - разные мастера, которым должен служить ваш код. Ни одна строка кода не может долго обслуживать более одного мастера.

candied_orange
источник
Прежде всего, большое спасибо всем за все комментарии.
user2549686