О плюсах инверсии зависимостей и той гибкости, которую она даёт приложениям сказано очень многое, и я искренне надеюсь, что она повсеместно используется :)
Как известно, существует несколько способов инверсии зависимостей:
- Constructor-based injection — инъекция через конструктор. Класс объявляет свои зависимости как параметры в конструкторе:
public class MyImageProcessor { private readonly IScreenshooter _screenshooter; private readonly IImageResizer _imageResizer; private readonly IImageComparer _imageComparer; public MyImageProcessor(IScreenshooter screenshooter, IImageResizer imageResizer, IImageComparer imageComparer) { _screenshooter = screenshooter; _imageResizer = imageResizer; _imageComparer = imageComparer; } }
Процессор получает сервисы, которые ему нужны для работы через конструктор и в дальнейшем работает с ними.
- Property-based injection — инъекция через публичные свойства. Свойства в этом случае обычно необходимо украшать атрибутами:
public class MyImageProcessor2 { [Inject] public IScreenshooter Screenshooter { get; set; } [Inject] public IImageResizer ImageResizer { get; set; } [Inject] public IImageComparer ImageComparer { get; set; } public MyImageProcessor2() { } }
Выглядит короче, но имеет очевидные минусы — зависимости класса непонятны (легко забыть инициализировать свойства, например, из тестов; при инъекции через конструктор с этим проблем нет), доступ к сервисам «открыт внешнему миру», что также не всегда является желаемым поведением.
Есть еще field-injection, когда инъекция идет не через публичные свойства, а через публичные поля, но этот вариант уже почти официально считается «говнокодом» :)
До недавнего времени в подавляющем большинстве случаев я использовал инъекцию через конструктор, и с трудом представлял причины, по которым можно предпочесть property-injection.
Continue reading