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

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

May 10, 2015

Download

Technology

Pavel Egorov

Предпосылки для появления такой штуки, как Dependency Injection Container.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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