Как справиться с чрезмерной инъекцией конструктора в .NET.

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

Сегодня я открываю один из классов в моем фактическом приложении и обнаружил, что он имеет 13 зависимостей, введенных конструктором!!! На самом деле каждый разработчик добавил, что он нужен в методе, который он писал.

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

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

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

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

Ответ 1

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

В принципе, есть два способа решения этой проблемы:

  • Рефакторинг для служб фасадов. Это в основном неразрывный рефакторинг, поскольку он поддерживает функциональность контроллера. Однако он изменяет ответственность за координацию/организацию взаимодействия между службами вместо управления подробными подробностями реализации.
  • Разделить класс на независимые классы. Если каждая из служб была внедрена различными разработчиками для поддержки подмножества методов, это признак низкой сплоченности. В этом случае было бы лучше разбить класс на несколько независимых классов.

В частности, для ASP.NET MVC вы не можете разделить контроллер, потому что он изменит вашу схему URL. Это достаточно справедливо, но рассмотрим, что это означает: это означает, что единственная ответственность Контролера должна быть отображать URL-адреса для кода приложения. Другими словами, все, что должен сделать контроллер, а затем следует, что правильным решением является рефакторинг для служб фасадов.

Ответ 2

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

С другой стороны, если класс имеет 13 зависимостей, это может быть слишком много. Связаны ли какие-либо из этих зависимостей вместе? Возможно, сами зависимости должны быть "chunkier" - или, возможно, ваш класс должен делать меньше.

Ответ 3

Это звучит как случай класса, у которого слишком много зависимостей, т.е. это класс Бога. Попробуйте и разделите его на более дискретные обязанности.

Ответ 4

Если у вашего класса 13 зависимостей, у вас определенно есть проблема. Очевидно, что ваш класс выполняет слишком много обязанностей. Марк Семанн обсудил эту проблему в своей книге "Зависимость внедрения в .NET" в пункте 6.4. Если ваш класс начинает иметь 3 параметра в конструкторе, вы должны начать задаваться вопросом. Является ли мой класс еще одной ответственностью? Если вы начинаете иметь 4 или более параметров в своем конструкторе, реорганизуйте свой класс, используя фасад или шаблон композиции.

Ответ 5

Его просто способ DI работает. Это факт. Поэтому примите это. Это полностью законно в службе, например, службе Pdf. Посмотрите на этот код:

public PdfService(ILocalizationService localizationService, 
        ILanguageService languageService,
        IWorkContext workContext,
        IOrderService orderService,
        IPaymentService paymentService,
        IDateTimeHelper dateTimeHelper,
        IPriceFormatter priceFormatter,
        ICurrencyService currencyService, 
        IMeasureService measureService,
        IPictureService pictureService,
        IProductService productService, 
        IProductAttributeParser productAttributeParser,
        IStoreService storeService,
        IStoreContext storeContext,
        ISettingService settingContext,
        IAddressAttributeFormatter addressAttributeFormatter,
        CatalogSettings catalogSettings, 
        CurrencySettings currencySettings,
        MeasureSettings measureSettings,
        PdfSettings pdfSettings,
        TaxSettings taxSettings,
        AddressSettings addressSettings)