Главная страница

Внедрение зависимостей в. Внедрение зависимостей в .NET. Руководство по применению этого механизма. Net приложениях. Книга демонстрирует основные паттерны на обычном языке C#, поэтому вы в полной мере поймете, как работает механизм внедрения зависимостей


Скачать 5.66 Mb.
НазваниеРуководство по применению этого механизма. Net приложениях. Книга демонстрирует основные паттерны на обычном языке C#, поэтому вы в полной мере поймете, как работает механизм внедрения зависимостей
АнкорВнедрение зависимостей в .net
Дата14.12.2019
Размер5.66 Mb.
Формат файлаpdf
Имя файлаВнедрение зависимостей в .NET.pdf
ТипРуководство
#100226
страница20 из 43
1   ...   16   17   18   19   20   21   22   23   ...   43
когда корзина в последний раз обновлялась,
кто является владельцем корзины, а также об общ ей стоимости (включая скидки) корзины.
Т екущей датой выполнения этой конкретной сессии была 22 марта 2010 года. Обратите внимание на то, что второй корзине уже более 30 дней. Т еперь вы можете рассчитать дату закрытия на основании текущей даты и использовать ее в выражении фильтра. Вы можете удалить все старые корзины путем передачи результата
Get-Basket в фильтр, а затем передавая результат отфильтрованных корзин в cmdlet Remove-Basket
. Если бы вы захотели выполнить фильтрацию по свойству
Total
, то вы также смогли бы это сделать тем же способом.
В итоге, вы перечисляете все корзины, чтобы удостовериться, что все старые корзины удалены.
П римечание
Не переживайте, если не поняли всех деталей выражения фильтра. Это книга не о
PowerShell, поэтому я не буду задерживаться на этой теме.

276
Для продвижения этого API написания сценариев вам необходимо реализовать два пользовательских cmdlet
'а. Поскольку одним из требований является то, что
Total должен принимать во внимание все соответствующие бизнес-правила, вам необходимо компоновать cmdlet
'ы в рамках уровня Domain Model.
Построение G etBasketCmdlet
Давайте рассмотрим, как реализуется cmdlet Get-Basket
Remove-Basket реализуется похожим способом, поэтому я не буду рассматривать его реализацию.
Для того чтобы избежать соблазна статического контейнера, вы будете реализовывать полноценный мост между cmdlet
'ом PowerShell и Domain Model в отдельной библиотеке, которая носит название
BasketPowerShellLogic
. Рисунок 7-18 демонстрирует, как компонуется приложение в пределах библиотек.
Рисунок 7-18: Библиотека
BasketPowerShell содержит только инфраструктуру, необходимую для того, чтобы осчастливить PowerShell – это humble-объект. Как только
BasketContainer преобразовал
BasketManager
, все дальнейшие реализации происходят в других сборках. Класс
BasketManager не имеет доступа к внутреннему
BasketContainer
, но использует
IBasketService из Dom ain Model. Обычно стрелки обозначают указатели.
Не все рассматриваемые классы продемонстрированы на рисунке.
Примечание
Если вы думаете, что рисунок 7-18 очень похож на рисунок 7-16, то вы начинаете понимать паттерн.
Примечание
Вы можете вспомнить
IBasketService из главы 2, раздела 2.3.2.

277
Класс
GetBasketCmdlet должен иметь конструктор по умолчанию для того, чтобы соответствовать PowerShell, поэтому вы используете его в качестве Com position Root и оставляете его в виде hum ble-объекта. Следующий листинг демонстрирует только то, насколько он "скромен".
Листинг 7-15: Реализация
GetBasketCmdlet
1.
[Cmdlet(VerbsCommon.Get, "Basket")]
2.
public class GetBasketCmdlet : Cmdlet
3.
{
4.
private readonly BasketManager basketManager;
5.
public GetBasketCmdlet()
6.
{
7.
this.basketManager =
8.
BasketContainer.ResolveManager();
9.
}
10.
protected override void ProcessRecord()
11.
{
12.
var baskets =
13.
this.basketManager.GetAllBaskets();
14.
this.WriteObject(baskets, true);
15.
}
16.
}
Строка 7-8: Com position Root
Строка 12-13: Делегирование полномочий реализатору
В требуемом конструкторе по умолчанию вы используете статический контейнер для того, чтобы преобразовать
BasketManager
, который выступает в роли реализации.
BasketManager использует Constructor Injection для запроса экземпляра
IBasketService
К настоящему моменту вы должны уже хорошо знать этот паттерн, а также реализацию
BasketContainer
, продемонстрированну ю в следующем листинге.
Листинг 7-16: Преобразование
BasketManager
1.
internal static BasketManager ResolveManager()
2.
{
3.
BasketRepository basketRepository =
4.
new SqlBasketRepository(
5.
BasketContainer.connectionString);
6.
DiscountRepository discountRepository =
7.
new SqlDiscountRepository(
8.
BasketContainer.connectionString);
9.
BasketDiscountPolicy discountPolicy =
10.
new RepositoryBasketDiscountPolicy(
11.
discountRepository);
12.
IBasketService basketService =
13.
new BasketService(basketRepository,
14.
discountPolicy);
15.
return new BasketManager(basketService);
16.
}
Строка 1: Внутренний метод
Строка 15: Возвращает basket manager

278
Метод, так же, как и весь класс, является внутренним, что делает возможным вызов его из
GetBasketCmdlet
, как это продемонстрировано в листинге 7-15, но невозможно случайно использовать его из
BasketManager или из его зависимостей.
Т еперь реализация метода должна быть вам понятной. И снова я считаю, что проще всего отойти от результата. Для класса
BasketManager необходим экземпляр
IBasketService
, и поэтому вы используете класс
BasketService
(других реализаций, которые вы могли бы выбрать, у вас нет).
Для
BasketService необходимы
BasketRepository и
BasketDiscountPolicy
. Для
BasketDiscountPolicy вы используете
RepositoryBasketDiscountPolicy
. Для этого класса требуется еще одна абстракция репозитория, а для этих двух репозиториев вы используете реализации на основе SQL Server.
Реализация
BasketManager является стандартной, поэтому я не буду ее показывать. Все, что она делает – выражает необходимые операции в терминах доменной модели.
Remove-Basket cmdlet руководствуется тем же паттерном: он использует статический, но внутренний
BasketContainer для того, чтобы преобразовать экземпляр
BasketManager
, а потом делегировать реализацию преобразованному экземпляру. Оба cm dlet'а выступают в роли сочетания Composition Root и hum ble-объекта.
Класс
BasketManager реализуется в другой сборке. Как только код уходит от cmdlet'ов, риск, что какая-либо из основополагающих реализаций будет использовать статический контейнер в качестве Service Locator, исчезает, поскольку он является внутренним по отношению к сборке, содержащей cm dlet'ы.
П римечание
Очевидно, основополагающий код ничего не будет делать случайно, но разработчик, пишущий код, может. Мы защищаем статический контейнер от остальной части кода для того, чтобы защитить себя от совершения ошибок.
Фреймворк, подобный PowerShell, является самым DI-недружест венным. Использование простой технологии превращения каждого элемента фреймворка в Com position Root и
humble-объект дает вам простой способ решения этой проблемы.

279 7.7. Резюме
Композиция объектов– один из трех важных аспектов механизма внедрения зависимостей
(двумя другими являются управление жизненным циклом и перехват). В данной главе я продемонстрировал то, как компоновать приложения из слабо связанных модулей во множестве различных сред.
Некоторые фреймворки облегчают этот процесс. При написании консольных приложений и Windows клиентов (WPF или W indows Forms) мы более или менее напрямую контролируем то, что происходит в точке входа в приложение. Это обеспечивает нас различными и легко реализуемыми Com position Root в точке входа.
Другие фреймворки, например, ASP.NET MVC и WCF, заставляют нас поработать немного усерднее, но они все же предоставляют швы, которые мы можем использовать для того, чтобы определить то, как приложение должно быть скомпоновано. ASP.NET
MVC уже был создан с замыслом механизма внедрения зависимостей, поэтому построение приложения такой же простой процесс, как и реализация пользовательского
IControllerFactory и регистрация его с помощью фреймворка. Кажется, что в W CF шов находится почти случайно, но, несмотря на то, что это более обходной путь, нежели реализация единичного интерфейса, мы все еще можем достичь всех ценных свойств механизма внедрения зависимостей, которые только могли бы пожелать.
Остальные фреймворки являются явно DI-недружественными и требуют от нас использования конструктора по умолчанию для соответствия им. ASP.NET (W eb Form s) является самым известным из них, но еще одними примерами являются также и
PowerShell, и Managed MMC SDK. Эти фреймворки управляют жизненными циклами классов, которые мы предоставляем, поэтому единственный вариант – рассматривать каждый класс как отдельный Com position Root. Это требует много затрат, поэтому я лично предпочитаю использовать DI-дружественные фреймворки, если у меня есть выбор.
Без композиции объектов нет и механизма внедрения зависимостей, но вы, возможно, еще не осознали полностью роль жизненного цикла объектов, когда мы переносили создание объектов из используемых классов. Вам может показаться само собой разумеющимся, что внешний объект, выполняющий вызов, (чаще всего DI-контейнер) создает новые экземпляр ы зависимостей – но когда высвобождаются внедряемые зависимости? И что если внешний объект, выполняющий вызов, решит не создавать новые экземпляры все время, а вместо этого передаст вам существующий экземпляр? Это темы обсуждения для следующей главы.

280 8. Жизненный цикл объектов
Меню:

Управление жизненным циклом зависимостей

Устраняемые зависимости

Singleton

T ransient

Per Graph

W eb Request Context

Pooled
Истечение срока действия основательно влияет на большинство продуктов и напитков, но последствия сильно различаются. Лично я считаю 12-месячный Gruyère (швейцарский сыр) гораздо более интересным, нежели 6-месячный Gruyère, но предпочитаю, чтобы аспарагус был свежее 12-месячного и 6-месячного Gruyère. В большинстве случаев оценить точный возраст элемента довольно просто; но в определенных случаях этот процесс становится очень сложным. Истечение срока действия наиболее значимо, когда дело касается вина (см. рисунок 8-1).
Рисунок 8-1: Вино, сыр и аспарагус. Несмотря на то, что, если смешать эти ингредиенты мы получим не совсем хороший результат, возраст этих ингредиентов значительно влияет на их общее качество.

281
С годами вина становятся лучше – до тех пор, пока они не станут слишком старыми и не потеряют свой аромат. Это зависит от множества факторов, включая происхождение и урожай вина. Несмотря на то, что я интересуюсь винами, я даже и не надеюсь, что смогу определить, когда вино достигнет своего полного расцвета. В этом вопросе я полагаюсь на экспертов: на книги – дома и на сомелье – в ресторанах. Они разбираются в винах лучше меня, так как это их специальность, поэтому всякий раз, когда я им доверяюсь в вопросе выбора вина, я с удовольствием позволяю им взять все под свой контроль.
За исключением того случая, когда вы перешли к данной главе, не читая при этом ни одной из предыдущих глав, вы понимаете, что возможность уйти от контроля является ключевой концепцией механизма внедрения зависимостей. Это проблема инверсии управления, но она подразумевает больше, чем просто позволение кому-то отобрать реализацию необходимой абстракции. Когда мы позволяем
Composer
(Компоновщик) создавать зависимость, мы также должны принять тот факт, что не сможем контролировать ее жизненный цикл.
Поскольку сомелье хорошо знает содержания винных подвалов ресторана и может принять гораздо более информативное решение, нежели бы это сделали мы, способность контролировать жизненный цикл зависимостей мы должны с наибольшей эффективност ью доверять
Composer
(Компоновщик), чем потребителю. Компоновка и управление компонентами – это единичная ответственность.
О пределение
Composer
– в том смысле, в котором я его использую здесь, это унифицированный термин, обозначающий любой объект или метод, который компонует зависимости. Чаще всего это
DI-контейнер, но может быть и любым методом, который используется в Poor Man's DI, например, метод
Main консольного приложения.
В данной главе мы будем рассматривать процесс управления жизненным циклом зависимостей. Понимание этой темы важно, поскольку точно так же, как вы можете приобретать неудовлетворительный опыт при употреблении вина неправильного возраста, вы можете получить ухудшенное выполнение, неправильно сконфигурировав жизненный цикл зависимостей. Даже хуже того, вы можете получить аналог испортившегося продукта для механизма управления жизненным циклом: утечки ресурсов. Понимание принципов корректного управления границами жизненных циклов компонентов должно дать вам возможность принимать правильные решения и корректно конфигурировать ваши приложения.
П римечание
На всем протяжении данной главы я использую термины тип ст иля сущест вования,
стратегия жизненного цикла, границы жизненного цикла и другие редко встречающиеся сочетания взаимозаменяемо.
Как иллюстрирует рисунок 8-2, мы начнем с общего введения к концепции, за которым следует обсуждение устраняемых зависимостей. Первая часть главы предназначена для того, чтобы предоставить вам всю теоретическую информацию и необходимые вам ведущие принципы для того, чтобы принимать умное, взвешенное решение по поводу конфигурации границ жизненного цикла ваших собственных приложений.

282
Рисунок 8-2: Полная структура данной главы. Мы начнем с общего обсуждения процесса управления жизненным циклом зависимостей, включая конкретное обсуждение работы с устраняемыми объектами. Нам необходимы эти фундаментальные знания для того, чтобы эффективно обсуждать универсальные паттерны небольшого каталога стилей существования, рассматриваемого далее. Мы начнем с рассмотрения некоторых универсальных и очень полезных паттернов и закончим кратким обзором некоторых более экзотичных стилей существования для того, чтобы дать вам ощущение широты данной темы.
После этого мы будем использовать остальную часть главы для рассмотрения различных стратегий жизненного цикла. Эта часть книги принимает форму каталога доступных стилей существования. В большинстве случаев один из этих шаблонных паттернов стилей существования будет отлично подходить для решения конкретной задачи, поэтому заблаговременное понимание этих стилей снабжает вас достаточными знаниями для того, чтобы справляться со многими сложными ситуациями. Когда мы закончим изучение этой главы, вы должны будете хорошо разбираться в управлении жизненном циклом и в универсальных жизненных циклах.
Для начала давайте рассмотрим жизненный цикл объектов и то, каким образом он в общем смысле относится к механизму внедрения зависимостей.
8.1. Управление жизненным циклом зависимостей
8.2. Работа с устраняемыми зависимостями
8.3. Каталог стилей существования объектов
8.4. Резюме

283 8.1. Управление жизненным циклом зависимостей
До настоящего момента мы, главным образом, обсуждали то, как механизм внедрения зависимостей позволяет нам компоновать зависимости. В предыдущей главе рассматривался этот вопрос чрезвычайно подробно, но как я говорил в разделе 1-4, композиция объектов является всего лишь одним из аспектов механизма внедрения зависимостей. Управление жизненным циклом объектов является еще одним его аспектом.
П римечание
В .NET жизненный цикл объекта достаточно прост: объект создается, используется и уничтожается сборщиком мусора (garbage collector). Присутствие
IDisposable слегка все усложняет, но жизненный цикл от этого не становится более сложным. При обсуждении жизненного цикла объектов мы говорим о том, как мы управляем жизненными циклами объектов.
В первый раз, когда я познакомился с идеей о том, что в сферу механизма внедрения зависимостей входит управление жизненным циклом, я не понимал глубокой связи между композицией объектов и жизненном циклом объектов. В конце концов, я это осознал, и это оказалось довольно просто, поэтому давайте рассмотрим это вместе с вами.
В данном разделе я буду знакомить вас с механизмом управления жизненным циклом и с тем, как применить его к зависимостям. Мы начнем с рассмотрения общего случая компоновки объектов и того, как компоновка влияет на жизненные циклы зависимостей.
От этой темы мы перейдем к изучению того, как DI-контейнеры могут управлять жизненным циклом зависимостей. Несмотря на то, что большинство из примеров являются специализированным кодом, который имеет дело с конкретными конфигурациями, мы также сделаем краткий обзор шаблонного DI-контейнера для того, чтобы получить представление о том, как может выглядет ь конфигурация жизненного цикла.
Для начала мы исследуем, почему композиция объектов влияет на жизненный цикл.
Знакомство с механизмом управления жизненным циклом
Когда мы принимаем тот факт, что нам следует выбросить из головы наши физиологические потребности для того, чтобы контролировать зависимости и, кроме того, запрашивать их с помощью Constructor Injection или одного из других DI-паттернов, мы должны выбросить это из головы окончательно. Для того чтобы понять, почему, мы рассмотрим этот вопрос постепенно. Давайте начнем с обзора того, что означает стандартный жизненный цикл объекта для зависимостей. Вы уже должны это знать, но потерпите, пока я не пройду следующую половину страницы, и не установлю контекст.
П ростой жизненны й цикл зависимосте й
Вы знаете, что механизм внедрения зависимостей подразумевает, что мы позволяем сторонним объектам выступать для нас в роли необходимых нам зависимостей. Это также означает, что мы должны позволить им управлять жизненными циклами зависимостей.

284
Проще всего понять это, когда дело доходит до создания объекта. Ниже приведен фрагмент кода Composition Root шаблонного приложения Com merce (окончательный пример вы можете увидеть в листинге 7-3). var discountRepository = new SqlDiscountRepository(connectionString); var discountPolicy = new RepositoryBasketDiscountPolicy(discountRepository);
Надеюсь, это очевидно, что класс
RepositoryBasketDiscountPolicy не контролирует то, когда создается discountRepository
. В данном случае это, скорее всего, происходит в пределах той же миллисекунды; но для чистоты эксперимента мы могли бы вставить вызов
Thread.Sleep между этими двумя строками кода для того, чтобы продемонстрировать, что мы можем условно разделить их на промежутки времени. Это было бы достаточно непонятно для исполнения, но вы все поняли.
Потребители не контролируют
1   ...   16   17   18   19   20   21   22   23   ...   43


написать администратору сайта