Основы Di контейнеров

27
Предпосылки появления DI-контейнеров Уральская группа пользователей .NET, март 2010 Павел Егоров, СКБ Контур [email protected] http://xoposhiy.livejournal.com

description

Предпосылки для появления такой штуки, как Dependency Injection Container.

Transcript of Основы Di контейнеров

Page 1: Основы Di контейнеров

Предпосылки появления DI-контейнеров

Уральская группа пользователей .NET, март 2010

Павел Егоров, СКБ Контур [email protected]

http://xoposhiy.livejournal.com

Page 2: Основы Di контейнеров

S

O

L

I

D

RP — Single Responsibility Principle

CP — Open Closed Principle

SP — Liskov Substitution Principle

SP — Interface Segregation Principle

IP — Dependency Inversion Principle

Page 3: Основы Di контейнеров

— Хотим спроектировать хорошо!

— Быть готовым к грядущим изменениям!

Page 4: Основы Di контейнеров

SOLID

RP — Single Responsibility Principle

Модуль =

= одна обязанность =

= одна причина изменения

пихать все в один класс — НЕТ! :-\

плодить много мелких классов — ДА :-)

Page 5: Основы Di контейнеров

Iso8859Decoder

InvoiceConverter

Utf8Decoder

SRP всех вылечит! :-)

Page 6: Основы Di контейнеров

Iso8859Decoder

Invoice

Converter

Utf8Decoder

void Convert(){ var decoder = new Iso8859Decoder(); … decoder.Decode(…); …}

Page 7: Основы Di контейнеров

SOLIDIP — Dependency Inversion Principle

Классы должны зависеть от интерфейсов, а не от других классов.

InvoiceConverte

r

IDecoder

Iso8859Decoder Utf8Decoder…

Page 8: Основы Di контейнеров

public InvoiceConverter(IDecoder decoder)

{ this.decoder = decoder;}

public void Convert(){ … decoder.Decode(…); …}

SRP + DIP всех вылечат! :-)

new InvoiceConverter(new Iso8859Decoder());

или

new InvoiceConverter(new Utf8Decoder());

InvoiceConverte

r

IDecoder

Iso8859Decoder Utf8Decoder

Page 9: Основы Di контейнеров

реализация интерфейса использование

SRP

DIP

Page 10: Основы Di контейнеров

Miško Heverymisko.hevery.com

— Давайте жестко разделять:

код, содержащий логику программы

код, вызывающий конструкторы

new A(new B(…), new C(…))…new SmtpClient(smtpUrl);…new ConsoleLogger()…

Page 11: Основы Di контейнеров

IService

Locator

Page 12: Основы Di контейнеров

ServiceLocator

Page 13: Основы Di контейнеров

зависимости стали неявными…

хрупкость!

ServiceLocator

Page 14: Основы Di контейнеров

ServiceLocator

Page 15: Основы Di контейнеров

ServiceLocator

все зависят от ServiceLocator’а

жесткость!

Page 16: Основы Di контейнеров

Miško Heverymisko.hevery.com

Page 17: Основы Di контейнеров

Электронный бухгалтер

более 100 сервисов Rule of thumb:1 сервис на каждые 5-7 килобайт кодаДействительно МНОГО

серого тупого рутинного кода

Page 18: Основы Di контейнеров

время жизни

Автоматический поиск всех типов

с каждой новой «фичей» будет все хуже…Ленивая инициализация, коллекции, один объект на несколько интерфейсов, xml-конфигурирование, IDisposable, дочерние контейнеры, …

Page 19: Основы Di контейнеров

← ServiceLocator

Page 20: Основы Di контейнеров

ServiceLocator

Page 21: Основы Di контейнеров

Conventions over configuration(via RoboContainer)

Электронный бухгалтер

<castle> <components> <component id="JsonSerializer" service="SKBKontur.IB.Serialization.IDataSerializer, IB.Serialization" type="SKBKontur.IB.Serialization.JsonSerializer, IB.Serialization"> </component> <component id="BankReferenceSource" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceSource`1[[SKBKontur.IB.DataContracts.BankInfo, IB.DataContracts]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Bank.BankReferenceSource, IB.ReferenceService, Version=1.0.0.0, Culture=neutral" > </component> <component id="BankReferenceImplementation" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceImplementation`2[[SKBKontur.IB.DataContracts.BankInfo, IB.DataContracts], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Bank.BankReferenceImplementation, IB.ReferenceService" > </component> <component id="BankReference" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReference`2[[SKBKontur.IB.DataContracts.BankInfo, IB.DataContracts], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Reference`2[[SKBKontur.IB.DataContracts.BankInfo, IB.DataContracts], [System.String]], IB.ReferenceService" > </component> <component id="BankReferenceWriter" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceServiceWriter, IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Bank.BankReferenceServiceWriter, IB.ReferenceService" > <parameters> <dataSerializer>${JsonSerializer}</dataSerializer> </parameters> </component>

<component id="LocationReferenceSource" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceSource`1[[SKBKontur.IB.DataContracts.Address.LocationInfo, IB.DataContracts]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.Location.LocationReferenceSource, IB.ReferenceService, Version=1.0.0.0, Culture=neutral" > </component> <component id="LocationReferenceImplementation" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceImplementation`2[[SKBKontur.IB.DataContracts.Address.LocationInfo, IB.DataContracts], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.Location.LocationReferenceImplementation, IB.ReferenceService" > </component> <component id="LocationReference" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReference`2[[SKBKontur.IB.DataContracts.Address.LocationInfo, IB.DataContracts], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Reference`2[[SKBKontur.IB.DataContracts.Address.LocationInfo, IB.DataContracts, Version=1.0.0.0, Culture=neutral], [System.String]], IB.ReferenceService" > </component> <component id="LocationReferenceWriter" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceServiceWriter, IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.Location.LocationReferenceServiceWriter, IB.ReferenceService" > <parameters> <dataSerializer>${JsonSerializer}</dataSerializer> </parameters> </component>

<component id="StreetReferenceSource" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceSource`1[[SKBKontur.IB.ReferenceService.KLADR.Street.StreetInfo, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.Street.StreetReferenceSource, IB.ReferenceService, Version=1.0.0.0, Culture=neutral" > </component> <component id="StreetReferenceImplementation" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceImplementation`2[[SKBKontur.IB.ReferenceService.KLADR.Street.StreetInfo, IB.ReferenceService], [SKBKontur.IB.ReferenceService.KLADR.Street.StreetReferenceSearchParams, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.Street.StreetReferenceImplementation, IB.ReferenceService" > </component> <component id="StreetReference" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReference`2[[SKBKontur.IB.ReferenceService.KLADR.Street.StreetInfo, IB.ReferenceService], [SKBKontur.IB.ReferenceService.KLADR.Street.StreetReferenceSearchParams, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Reference`2[[SKBKontur.IB.ReferenceService.KLADR.Street.StreetInfo, IB.ReferenceService], [SKBKontur.IB.ReferenceService.KLADR.Street.StreetReferenceSearchParams, IB.ReferenceService, Version=1.0.0.0, Culture=neutral]], IB.ReferenceService" > </component> <component id="StreetReferenceWriter" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceServiceWriter, IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.Street.StreetReferenceServiceWriter, IB.ReferenceService" > <parameters> <dataSerializer>${JsonSerializer}</dataSerializer> </parameters> </component>

<component id="HouseReferenceSource" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceSource`1[[SKBKontur.IB.ReferenceService.KLADR.House.HouseInfo, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.House.HouseReferenceSource, IB.ReferenceService, Version=1.0.0.0, Culture=neutral" > </component> <component id="HouseReferenceImplementation" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceImplementation`2[[SKBKontur.IB.ReferenceService.KLADR.House.HouseInfo, IB.ReferenceService], [SKBKontur.IB.ReferenceService.KLADR.House.HouseReferenceSearchParams, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.House.HouseReferenceImplementation, IB.ReferenceService" > </component> <component id="HouseReference" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReference`2[[SKBKontur.IB.ReferenceService.KLADR.House.HouseInfo, IB.ReferenceService], [SKBKontur.IB.ReferenceService.KLADR.House.HouseReferenceSearchParams, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Reference`2[[SKBKontur.IB.ReferenceService.KLADR.House.HouseInfo, IB.ReferenceService], [SKBKontur.IB.ReferenceService.KLADR.House.HouseReferenceSearchParams, IB.ReferenceService, Version=1.0.0.0, Culture=neutral]], IB.ReferenceService" > </component> <component id="HouseReferenceWriter" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceServiceWriter, IB.ReferenceService" type="SKBKontur.IB.ReferenceService.KLADR.House.HouseReferenceServiceWriter, IB.ReferenceService" > <parameters> <dataSerializer>${JsonSerializer}</dataSerializer> </parameters> </component>

<component id="IFNSReferenceSource" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceSource`1[[SKBKontur.IB.DataContracts.IFNSInfo, IB.DataContracts]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.IFNS.IFNSReferenceSource, IB.ReferenceService, Version=1.0.0.0, Culture=neutral" > </component> <component id="IFNSReferenceImplementation" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceImplementation`2[[SKBKontur.IB.DataContracts.IFNSInfo, IB.DataContracts], [SKBKontur.IB.ReferenceService.IFNS.IFNSReferenceSearchParams, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.IFNS.IFNSReferenceImplementation, IB.ReferenceService" > </component> <component id="IFNSReference" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReference`2[[SKBKontur.IB.DataContracts.IFNSInfo, IB.DataContracts], [SKBKontur.IB.ReferenceService.IFNS.IFNSReferenceSearchParams, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Reference`2[[SKBKontur.IB.DataContracts.IFNSInfo, IB.DataContracts], [SKBKontur.IB.ReferenceService.IFNS.IFNSReferenceSearchParams, IB.ReferenceService]], IB.ReferenceService" > </component> <component id="IFNSReferenceWriter" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceServiceWriter, IB.ReferenceService" type="SKBKontur.IB.ReferenceService.IFNS.IFNSReferenceServiceWriter, IB.ReferenceService" > <parameters> <dataSerializer>${JsonSerializer}</dataSerializer> </parameters> </component>

<component id="RegionReferenceSource" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceSource`1[[SKBKontur.IB.ReferenceService.Region.RegionInfo, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Region.RegionReferenceSource, IB.ReferenceService, Version=1.0.0.0, Culture=neutral" > </component> <component id="RegionReferenceImplementation" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceImplementation`2[[SKBKontur.IB.ReferenceService.Region.RegionInfo, IB.ReferenceService], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Region.RegionReferenceImplementation, IB.ReferenceService" > </component> <component id="RegionReference" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReference`2[[SKBKontur.IB.ReferenceService.Region.RegionInfo, IB.ReferenceService], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Reference`2[[SKBKontur.IB.ReferenceService.Region.RegionInfo, IB.ReferenceService], [System.String]], IB.ReferenceService" > </component>

<component id="VKTMOReferenceSource" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceSource`1[[SKBKontur.IB.ReferenceService.VKTMO.VKTMOInfo, IB.ReferenceService]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.VKTMO.VKTMOReferenceSource, IB.ReferenceService, Version=1.0.0.0, Culture=neutral"> </component> <component id="VKTMOReferenceImplementation" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceImplementation`2[[SKBKontur.IB.ReferenceService.VKTMO.VKTMOInfo, IB.ReferenceService], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.VKTMO.VKTMOReferenceImplementation, IB.ReferenceService" > </component> <component id="VKTMOReference" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReference`2[[SKBKontur.IB.ReferenceService.VKTMO.VKTMOInfo, IB.ReferenceService], [System.String]], IB.ReferenceService" type="SKBKontur.IB.ReferenceService.Reference`2[[SKBKontur.IB.ReferenceService.VKTMO.VKTMOInfo, IB.ReferenceService], [System.String]], IB.ReferenceService" > </component> <component id="VKTMOReferenceWriter" lifestyle="singleton" service="SKBKontur.IB.ReferenceService.IReferenceServiceWriter, IB.ReferenceService" type="SKBKontur.IB.ReferenceService.VKTMO.VKTMOReferenceServiceWriter, IB.ReferenceService" > <parameters> <dataSerializer>${JsonSerializer}</dataSerializer> </parameters> </component> </components> </castle>

Windsor containerxml-конфигурирование

модуль AJAX подсказок

160 строк,11 Кб

3 строки конфигурирования2 из которых «не по делу» и сигнализируют о «мертвом», неиспользуемом коде.

DisclaimerИспользуя Windsor containerтоже можно практиковать подходConvention over Configuration! Наверное…

Page 22: Основы Di контейнеров

Подытожим• Наш проект ждут изменения.• SRP+DIP дают нам защиту от изменений, но…• …принуждают к тому, чтобы писать много мелких

классов.• Из мелких классов где-то нужно собирать граф

объектов.• Контейнеры всего лишь упрощают эту сборку.• А с convention over configuration упрощают

радикально!

Вопросы

Page 23: Основы Di контейнеров

Но будет много противных аргументов конструктора!

обычно бывает так:

а вовсе не так:

секретный слайд

— Аргументов будет мало,это я вам гарантирую! ;-)

Page 24: Основы Di контейнеров

Может быть конфигурировать в конструкторе?

секретный слайд

задавать через GUI

не загружать дважды

Page 25: Основы Di контейнеров

Conventions — это путь к хрупкости

Conventions Configuration

Неявность :-( :-)

Рутина :-))) :-(((

Ошибки

секретный слайд

Page 26: Основы Di контейнеров

Robert C. Martinobjectmentor.com

http://objectmentor.com/resources/articles/Principles_and_Patterns.pdfDesign Principles and Design Patterns

Page 27: Основы Di контейнеров

Дизайн плох, если…

…надо много переделывать

…трогать код опасно!

…проще сделать «в обход»

…использовать готовое решение не получается

жесткость

хрупкость

нетехнологичность

немобильность

[когда приходит новая задача]

rigidity viscosity

fragility immobility