Как вы запрещаете занятиям стать "зависимыми магнитами" и божественными классами?

В практически каждом проекте, над которым я когда-либо работал, будет один или два класса со следующими свойствами:

  • Чрезвычайно большой со многими членами и методами.
  • Многие другие классы, наследующие от этого класса.
  • Многие другие классы в противном случае зависят от этого класса.

Плохой дизайн, можно сказать. Но во всех случаях это было не так во время разработки. Занятия со временем стали органично расти и стали пресловутым "классом Бога". Это был такой инвариант моего опыта работы с крупными проектами программного обеспечения, которые я должен задать:

  • Можно ли предвидеть вероятные магниты зависимостей и программное обеспечение для проектирования таким образом, чтобы вероятность того, что такие классы проявятся, менее вероятна? Если да, то конкретно, как?
  • Или это просто требует беспощадного рефакторинга с течением времени?
  • Или есть ли какое-то техническое решение или шаблон дизайна, который может смягчить проблемы, вызванные такими классами?
  • Или сочетание всех трех?

Подсказки, опыт и идеи приветствуются!

Ответ 1

Постоянно рефакторинг поможет предотвратить это.

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

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

Ответ 2

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

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

Эффективно существует уровень зрелости проекта, который увеличивается вместе с сложностью проекта; массово сложный дизайн для простого проекта неуместен; таким же образом, что массовый простой дизайн для сложного проекта неуместен. Но поскольку это несоответствие существует, дизайн должен развиваться вместе с проектом. Рефакторинг как процесс - это просто признание этой необходимости.

Ответ 4

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

Некоторые из шаблонов рефакторинга Фаулера полезны. Рефакторинг Extract Class может помочь, а может Извлечь подкласс и Извлечь суперкласс. Я часто нахожу, что меняю иерархии наследования, возможно, используя заменить наследование на делегирование.

Ответ 5

Безжалостный рефакторинг для меня тоже, особенно для небольших проектов (т.е. не спроектированных, водопадов), которые длится долго (например, годы).

Я начинаю с малого: немного архитектуры, скажем от 2 до 5 компонентов. Я реализую функциональность, добавляя код к этим компонентам... и когда компонент становится слишком большим (что субъективно), я разделяю его на несколько компонентов. [Под "компонентом" я имею в виду "пакет" или "DLL" или что-то еще.]

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