Pull to refresh

Comments 5

Спасибо за перевод.
Дино только почему то не рассмотрел один аспект DI.

На практике классы (в особенности сервисы) зависят от более чем одного класса, что приводит к тому, что конструктор объекта содержит много параметров. Это снижает удобство пользования классом.

public class MyClass
{
    public MyClass (IRepository rep, IService service, ILogger logger, ISettings settings, ICache cache, ITrace trace)
    ...
}


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

Проблему количества параметров в конструкторе обычно решают за счёт более умных библиотек DI, которые через рефлексию умеют разбирать конструктор и для каждого из параметров опять через себя создавать/находить экземпляры классов:

// регистрация - один раз на запуск приложения
builder.RegisterType<MyRepository> ().As<IRepository> ();
builder.RegisterType<MyService> ().As<IService> ();
builder.RegisterType<MyLogger> ().As<ILogger> ();
builder.RegisterType<MySettings> ().As<ISettings> ();
builder.RegisterType<MyCache> ().As<ICache> ();
builder.RegisterType<MyTrace> ().As<ITrace> ();
builder.RegisterType<MyClass> ();

// получение экземпляра класса
var obj = resolver.Resolve<MyClass> ();


Но, как мы понимаем, это повышает стоимость конструирования одного такого класса. На сколько? Если честно, я не смотрел код работы современных DI библиотек, но единственным способом сделать скорость приличной можно только если код для создания подобных классов конструировать сразу в MSIL «налету» и кэшировать полученный кусок CLR-кода для повтороного использования (т.е. для повтороного создания объекта того же типа).

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

В общем, это может быть для кого «preemptive optimization», но не лишним будет об этом помнить.

И ещё о чём не сказал Дино (об этом, вроде, Фаулер писал): главный плюс DI в том, что объект описанный подобным образом можно отдавать «другим» разработчикам — даже если у них не настроен DI (не зарегистрированны типы) можно будет создать экземпляр объекта самим. А вот с ServiceLocator нотацией будет сложнее — нужно будет ещё и инфрастуктуру для ServiceLocator настраивать.

В общем, ничего плохого в ServiceLocator нет, но рекомендуется на ServiceLocator писать только то, что будет использоваться внутри системы — это проще и лаконичнее. А DI вариант хорош прежде всего для тех, кто пишет библиотеки для сторонних приложений.
Хорошее и правильное дополнение. Спасибо.
На практике, если класс имеет слишком много зависимостей — это bad design. Чаще всего это свойственно каким-то божественным объектам, нарушающим SRP. В этому случае имеет смысл подумать над пересмотром архитектуры системы.
Ну, в моём примере можно выкинуть разве что Service — всё остальное вполне может быть нужно даже самой «чистой DDD» сущности, работающей в реалиях большой системы.
Sign up to leave a comment.

Articles