Стек зависимостей - Кодовый запах?
У меня есть настройки класса, такие как это:
class ClassToCheck(otherClass class2, nextClass class3)
class otherClass(thisClass class4, otherThisClass class5, popularClass class6)
class nextClass()
(это просто пример использования сигнатур конструктора классов)
Запах кода в том, что у классов есть много зависимостей, таких как эта? Как и в приведенном выше примере, ClassToCheck зависит от другого класса (и его зависимостей), а также от следующего класса (и его потенциальных зависимостей) и т. Д. И т. Д. И до абсурда.
Кажется, что зацикливание множества таких классов на разные классы само по себе может быть плохой практикой, у кого-нибудь есть совет для чего-то подобного?
4 ответа
Это, конечно, не обязательно должно быть A Bad Thing, а дизайн может быть A Good Thing. Программы сложные. Вы должны определить лучший способ выразить свои проекты.
Эта форма может указывать, что ваша программа правильно использует компоненты / реализации. Часто полезно использовать более точное представление типов, поскольку оно может упростить сложное.
Конечно, вы должны знать о затратах на внедрение зависимостей для клиентов (которые зависят от языка / библиотеки). В некоторых случаях вы должны минимизировать зависимость и отдавать предпочтение более примитивному / встроенному представлению.
Наконец, вы можете беспокоить своих клиентов, если вы не согласованы и используете типы из многих источников - поэтому API вашей библиотеки в идеале могут использовать один строковый тип для всех API (в качестве примера), а не наследовать строковый тип, используемый реализацией вашей программы.,
Марк Симанн предлагает четыре параметра в качестве разумного порога для конструктора 'over инъекция'. Ваш текущий максимум три, так что это кажется разумным.
Убедитесь, что вы вводите только "изменчивые" зависимости. Внедрение стабильных зависимостей просто создает шумный API и скрывает реальную ответственность класса.
Сеть зависимостей сама по себе не является плохой практикой. Однако, если вы обнаружите, что пытаетесь легко разрешить эти зависимости, то, возможно, ваш код шепчет вам, предполагая, что дизайн не совсем верен.
Классы нуждаются в сотрудниках, чтобы достичь чего-то функционального; следовательно, невозможно иметь много классов, которые не зависят друг от друга. Реальная проблема заключается в том, чтобы ввести зависимости, но все же управлять сложностью зависимостей. Вот несколько примеров того, что может пойти не так с зависимостями:
Циклически зависимая модульность - это происходит между двумя или более классами, когда они зависят друг от друга прямо или косвенно. Участвующие субъекты в цикле должны быть поняты, изменены и проверены вместе.
Hub-like Modularization - это происходит, когда класс имеет чрезмерные входящие и исходящие зависимости. Такие классы создают хрупкий дизайн, т. Е. Вы меняете такие классы, и вам, возможно, придется изменить много классов, которые зависят от него; Точно так же любой из классов, которые подобен хаб-классу, зависит от изменений, и вам, возможно, придется изменить хаб-подобный класс.
Эти запахи проектирования более подробно описаны в книге "Рефакторинг для запахов проектирования программного обеспечения".
Я слышал, что некоторые люди называют это "запахом конструктора".
Вы хотите, чтобы каждый класс служил одной ясной цели с минимальными зависимостями. Это обычно приводит к тому, что код выглядит как ваш. Я бы посмотрел на каждую зависимость: она действительно нужна? Это просто переадресация другому конструктору (в этом случае он должен быть перемещен за пределы класса). Как и все запахи, вам нужно посмотреть на конкретный случай, прежде чем вы сможете определить, хорошо это или плохо.
Я обычно пишу код, похожий на ваш, но добавляю конструктор по умолчанию, который создает объект с наиболее распространенными классами реализации. Это облегчает создание объекта даже для новичков в вашем коде, но добавляет зависимость к вашим классам реализации. Это компромисс, который я нашел, обычно работает хорошо.