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

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


Скачать 5.66 Mb.
НазваниеРуководство по применению этого механизма. Net приложениях. Книга демонстрирует основные паттерны на обычном языке C#, поэтому вы в полной мере поймете, как работает механизм внедрения зависимостей
АнкорВнедрение зависимостей в .net
Дата14.12.2019
Размер5.66 Mb.
Формат файлаpdf
Имя файлаВнедрение зависимостей в .NET.pdf
ТипРуководство
#100226
страница2 из 43
1   2   3   4   5   6   7   8   9   ...   43
Когда оно полезно?
Позднее связывание
Сервисы могут меняться местами с другими сервисами.
Ценится в стандартно м программном обеспечении, но, возможно, менее ценится в корпоративны х приложения х, в которых исполня ющая среда стремится к тому, чтобы быть хорошо определенной.
Расширяемость
Код можно расширять и использов ать заново с помощью явно не запланированных способов.
Ценится всегда
Параллельная разработк а
Код может разрабатыв ать ся параллельно.
Ценится в больших, сложных приложениях; но не так сильно в небольших, простых приложениях
Удобство сопровождения
Классы с явно определенными обязанностя ми легче поддержив ать.
Ценится всегда
Тестируемость
Классы можно тестировать модульно.
Ценится только, если вы выполняете модульное тестиров ание (а вы действитель но должны это делать)

22
П озднее связывание
При объяснении преимуществ программирования на основании интерфейсов и механизма внедрения зависимостей возможность заменять один сервис другим является самым преобладающим преимуществом для большинства людей, поэтому они стремятся взвесить все достоинства и недостатки, имея в виду только это преимущество.
Помните тот момент, когда я просил вас забыть все, что вы знали до этого, прежде чем начнете изучение? Вы можете сказать, что вы настолько хорошо знаете ваши потребности, что уверены в том, что вам никогда не придется заменять, скажем, вашу базу данных SQL
Server чем-то еще. Тем не менее, потребности изменяются.
NoSQ L, Windows Azure и аргументы в пользу композиции (composability)
Несколько лет назад я часто наталкивался на безразличные выражения, когда пытался убедить разработчиков и архитекторов в преимуществах механизма внедрения зависимостей.
"Допустим, таким образом, вы можете заменить ваш реляционный компонент доступа к данным чем-то другим. Чем?" Существует ли какая-нибудь альтернатива реляционных баз данных?"
XML файлы никогда не казались подходящей альтернативой для крупномасштабных корпоративных сценариев. За последнюю пару лет все значительно изменилось.
О Windows Azure было объявлено на конференции Microsoft PDC 2008, и эта платформа многое сделала для того, чтобы убедить даже консервативные организации, которые используют только продукцию Microsoft, в том, что необходимо переоценить их позицию касаемо хранилища данных. На данный момент существует реальная альтернатива реляционных баз данных, и мне приходится спрашивать людей только о том, хотели бы они, чтобы их приложение было "cloud-ready" приложением. Аргумент в пользу замещения на данный момент становится все весомее.
Связанное движение можно обнаружить во всей концепции NoSQL, которая моделирует приложения на основе ненормализованных данных – часто документо-ориентированных баз данных, но такие концепции, как Event Sourcing, также становятся все более важными.
В разделе "Код приложения "Hello DI!"" вы не использовали "позднее связывание", поскольку вы явно создавали новый экземпляр
IMessageWriter при помощи жестко- закодированного создания нового экземпляра
ConsoleMessageWriter
. Т ем не менее, вы можете ввести "позднее связывание" путем изменения только одного фрагмента кода. Вам нужно всего лишь изменить следующую строку кода:
IMessageWriter writer = new ConsoleMessageWriter();
Для того чтобы разрешить позднее связывание, вы могли бы заменить эту строку кода чем-то вроде этого: var typeName = ConfigurationManager.AppSettings["messageWriter"]; var type = Type.GetType(typeName, true);
IMessageWriter writer = (IMessageWriter)Activator.CreateInstance(type);

23
Посредством вытаскивания имени типа из конфигурационного файла приложения и создания из него экземпляра
Type вы можете использовать рефлексию для создания экземпляра
IMessageWriter во время компиляции без знания конкретного типа.
Чтобы выполнить это, вы указываете имя типа при настройке приложения messageWriter в конфигурационном файле этого приложения:



П редупреждение
В этом примере для доказательства используются некоторые кратчайшие пути. В действительности на этот пример оказывает негативное влияние анти-паттерн
C onstrai ned Construction , который подробно рассматривается в главе 5.
Слабое связывание разрешает "позднее связывание", поскольку существует только одно место, где вы создаете экземпляр
IMessageWriter
. В связи с тем, что класс
Salutation работает только по отношению к интерфейсу
IMessageWriter
, он никогда не замечает разницы.
В примере "Hello DI" "позднее связывание" будет предоставлять вам возможность писать сообщения другим адресатам, а не только в консоль – например, в базу данных или файл.
Можно добавлять такие возможности, даже если вы явно не планировали их до этого.
Расширяемость
Успешное программное обеспечение должно быть способно к модификации. Вам может понадобиться добавить в него новые возможности, а также расширить существующие.
Слабое связывание позволяет вам эффективно перестраивать приложение, подобно тому, как мы можем заново подключать электрические устройства при помощи вилок и розеток.
Давайте скажем, что вы хотите сделать пример "Hello DI" более безопасным, разрешая только авторизованным пользователям писать сообщения. Следующий листинг демонстрирует, как вы можете добавить эту возможность без изменения какой-либо существующей возможности: вы добавляете новую реализацию интерфейса
IMessageWriter
Листинг 1-2: Расширение приложения "Hello DI" путем добавления возможности обеспечения безопасности
1.
public class SecureMessageWriter : IMessageWriter
2.
{
3.
private readonly IMessageWriter writer;
4.
public SecureMessageWriter(IMessageWriter writer)
5.
{
6.
if (writer == null)
7.
{
8.
throw new ArgumentNullException("writer");
9.
}
10.
this.writer = writer;
11.
}
12.
public void Write(string message)

24 13.
{
14.
if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
15.
{
16.
this.writer.Write(message);
17.
}
18.
}
19.
}
Строка 14: Проверяет наличие авторизации
Строка 16: Записывает сообщение
Класс
SecureMessageWriter реализует интерфейс
IMessageWriter и в то же время использует его: он использует механизм внедрения зависимости через конструктор для того, чтобы запросить экземпляр
IMessageWriter
. Это стандартное приложение паттерна проектирования Decorator, о котором я упоминал в разделе "Осознание цели DI". Более подробно об этом паттерне мы поговорим в главе 9.
При реализации метода
Write сначала проверяется, авторизован ли текущий пользователь.
Т олько в этом случае этому пользователю разрешается записывать сообщения в поле writer с помощью метода
Write
П римечание
Метод
Write в листинге 1-2 обращается к текущему пользователю через Ambient Con text
(окружающий контекст). Более гибкий, но в то же время немного более сложный вариант также мог бы предоставить пользователя посредством внедрения через конструктор.
Единственное место, где вам нужно будет изменить существующий код – это метод
Main
, поскольку вам нужно скомпоновать доступные классы несколько другим способом, нежели вы это делали до этого:
IMessageWriter writer = new SecureMessageWriter( new ConsoleMessageWriter());
Заметьте, что вы награждаете предыдущий экземпляр
ConsoleMessageWriter классом
SecureMessageWriter
. В очередной раз класс
Salutation не модифицируется, поскольку он использует только интерфейс
IMessageWriter
Слабое связывание позволяет вам писать код, который открыт для расширяемости, но закрыт для модификации. Это называется принципом от крытости/закрытости
(Open/closed principle). Единственное место, где вам нужно модифицироват ь код – в точке входа приложения; мы называем ее Com position Root.
SecureMessageWriter реализует возможность обеспечения безопасности в приложения, в то время, как
ConsoleMessageWriter обращается к пользовательскому интерфейсу. Это позволяет нам варьировать эти аспекты независимо друг от друга и компоновать их так, как нам нужно.
П аралле льная разработка

25
Концепция разделения делает возможным разработку кода параллельными командами.
Когда проект разработки программного обеспечения достигает определенного размера, становится необходимым разделить команду разработчиков на несколько команд легко управляемого размера. За каждой командой закрепляется ответственность за некоторую область приложения.
Для того чтобы разграничить ответственности, каждая команда будет разрабатывать один или более одного модуля, которые нужно будет интегрировать в законченное приложение.
За исключением тех случаев, когда области деятельности каждой команды точно не зависят друг от друга, некоторые команды, скорее всего, зависят от функционально сти, которая разрабатывается другими командами.
В приведенном выше примере благодаря тому, что классы
SecureMessageWriter и
ConsoleMessageWriter не зависят друг от друга напрямую, они могут разрабатываться параллельными командами. Момент, который им нужно будет согласовывать – это совместно используемый интерфейс
IMessageWriter
Удобство сопровождения
Как только ответственность каждого класса становится явно определенной и ограниченной, процесс сопровождения всего приложения облегчается. Это хорошо известное преимущество принципа единст венной от вет ст венности (single
responsibility principle), который утверждает, что каждый класс должен обладать только единственной ответственностью.
Процесс добавления новых возможностей в приложение упрощается, поскольку совершенно ясно, где нужно выполнить изменения. Почти всегда нам даже не нужно изменять существующий код, но вместо этого мы можем добавлять новые классы и заново компоновать приложение. Здесь в игру снова вступает принцип единственной ответственности.
Поиск неисправностей также становится менее утомительным, поскольку область возможных виновников неисправностей сужается. Благодаря явно определенным ответственностям у вас всегда будет четкое осознание того, откуда нужно начинать поиск корневой причины появления проблемы.
Тестируе мость
Для некоторых тестируемость является наименьшим поводом для беспокойства; для других это абсолютная потребность. Лично я принадлежу к последней категории людей: за мою карьеру я отклонил несколько предложений, потому что в них входила работа с определенными, нетестируемыми продуктами.
О пределение
Приложение считается тест ируемым, когда его можно тестировать помодульно.
Преимущество тестируемости, возможно, самое спорное из всех преимуществ, которые я перечислил ранее. Многие разработчики и архитекторы не практикуют модульное тестирование, поэтому они считают это преимущество в лучшем случае несущественным.
Другие, такие как я, считают его существенным. Майкл Физерс даже дает определение

26 термину "унаследованное приложение" как любое приложение, которое не охвачено модульным тестированием.
Практически случайно слабое связывание разрешает модульное тестирование, потому что пользователи руководствуются принципом замещения Лисков: они не заботятся о том, чтобы у их зависимостей были конкретные типы. Это означает, что мы можем внедрить
дублеры теста (T est Doubles) в т естируемую сист ему (System Under T est (SUT)), как мы это видим в листинге 1-3.
Возможность подменять заданную зависимость заменителем конкретного теста – это побочный результат слабого связывания, но я предпочел указать эту возможность как отдельное преимущество, поскольку унаследованное значение отличается от первоначального.
Тестируе мость
Т ермин "тестируемость" ужасно неопределенный, однако он широко используется в обществе разработчиков программного обеспечения, главным образом теми, кто практикует модульное тестирование.
В принципе любое приложение можно тестировать таким образом. Т есты могут выполняться людьми, которые используют это приложение, посредством их пользовательского интерфейса или какого-нибудь другого интерфейса, который предоставляет это приложение. Т акие ручные тесты требуют больших временных и денежных затрат, поэтому наиболее предпочтительным является автоматизированное тестирование.
Существует множество различных видов автоматизированного тестирования, например, модульное тестирование, интеграционное тестирование, тестирование продуктивности, нагрузочное тестирование (stress testing) и т.д. Поскольку модульное тестирование имеет небольшое количество требований к исполняющим средам, оно является самым эффективным и сильным видом теста; часто в этом контексте и оценивается тестируемость.
Модульные тесты обеспечивают быструю ответную реакцию на состояние приложения, но модульные тесты можно писать только тогда, когда тестируемый модуль может быть явно изолирован от его зависимостей. Существует некоторая неоднозначность того, насколько в действительности детализирован модуль, но все соглашаются, что это действительно не является тем, что соединяет множественные модули. Возможность тестировать модули изолированно друг от друга очень важна для модульного тестирования.
Т олько когда приложение поддается модульному тестированию, его можно считать тестируемым. Самый безопасный способ обеспечения тестируемости приложения – это разрабатывать приложение при помощи технологии т ест ирования через разработку
(T DD).
Необходимо отметить, что сами по себе модульные тесты не являются гарантией работоспособности приложения. Для того чтобы проверить, работает ли приложение так, как планировалось, все еще необходимы тесты всей системы или другие промежуточные виды тестов.

27
В зависимости от типа приложения, которое я разрабатываю, я могу заботиться, а могу и не заботиться о возможности выполнять "позднее связывание", но я всегда забочусь о тестируемости приложения. Некоторые разработчики не заботятся о тестируемости, но считают "позднее связывание" важным для разрабатываемого ими приложения.
Дублеры те ста
Это универсальная методика создания реализаций зависимостей, которые выступают в качестве заменителей реальных или планируемых реализаций. Т акие реализации называются дублерами тест а, и они никогда не будут использоваться в конечном приложении. Вместо этого они служат заполнителями для реальных зависимостей, когда они недоступны или не подходят для использования.
Для дублеров теста существует законченный язык паттернов и множество подтипов таких, как Stubs, Mocks и Fakes.
П ример: Модульное тестирование логики приложения "Hello"
В разделе "Код приложения "Hello DI!"" вы видели пример приложения "Hello DI".
Несмотря на то, что я сначала продемонстрировал вам конечный код, я, в действительности, разрабатывал это приложение при помощи тестирования через разработку. Листинг 1-3 демонстрирует самый важный модульный тест.
П римечание
Не волнуйтесь, если у вас нет опыта работы с модульным тестированием или динамическими mock-объектами. Они могут случайно всплывать на протяжении всей книги, но ни коим образом не являются обязательными для чтения.
Листинг 1-3: Модульное тестирование класса
Salutation
[Fact] public void ExclaimWillWriteCorrectMessageToMessageWriter()
{ var writerMock = new Mock(); var sut = new Salutation(writerMock.Object); sut.Exclaim(); writerMock.Verify(w => w.Write("Hello DI!"));
}
Для класса
Salutation требуется экземпляр интерфейса
IMessageWriter
, поэтому вам нужно его создать. Вы могли бы использовать любую реализацию, но в модульных тестах могут быть очень полезными динамические mock-объекты – в данном случае вы используете
Moq
, но могли бы использовать и другие библиотеки или вместо этого свернуть свою собственную. Важная составляющая – обеспечение реализации
IMessageWriter для конкретного теста с целью убедиться в том, что вы тестируете только один объект за раз; в настоящий момент вы тестируете метод
Exclaim класса
Salutation
, поэтому вы не хотите, чтобы какая-нибудь производственная реализация
IMessageWriter захламляла тест.
Чтобы создать класс
Salutation
, вы передаете
Mock
-экземпляр
IMessageWriter
Поскольку writerMock
– это экземпляр
Mock
, свойство
Object
– это динамически создаваемый экземпляр
IMessageWriter
. Внедрение нужной зависимости посредством конструктора носит название "внедрение через конструктор".

28
После применения тестируемой сист емы (System Under Test (SUT)) вы можете использовать
Mock
, чтобы проверить, что метод
Write был вызван с корректным текстом.
При использовании
Moq вы выполняете это путем вызова метода
Verify
, в качестве параметра которого задано выражение, которое определяет то, что вы запланировали.
Если метод
IMessageWriter.Write был вызван со строкой "Hello DI!", то вызов метода
Verify завершается, но если метод
Write не вызывался или вызывался с другим параметром, то метод
Verify выдавал бы исключение и тест бы не выполнялся.
Слабое связывание предоставляет множество преимуществ: код становится проще разрабатывать, поддерживать, расширять, и он становится более тестируемым. Слабое связывание даже не особо и сложное. Мы программируем на основании интерфейсов, а не конкретных реализаций. Единственный основной недостаток – понять, как достать экземпляр ы этих интерфейсов. Механизм внедрения зависимостей дает ответ на этот вопрос путем внедрения зависимостей из вне. Внедрение через конструктор – это наиболее предпочтительный метод внедрения зависимостей из вне.

29 1.3. Что внедрять, а что не внедрять
Базы данных – это отличный пример типов BCL, которые являются неустойчивыми зависимостями: даже если LINQ to Entities – это технология, которая содержится в BCL, ее использование подразумевает реляционную базу данных.
В предыдущем разделе я описывал мотивационные силы, которые заставляют нас в первую очередь думать о механизме внедрения зависимостей. Если я убедил вас в том, что слабое связывание – это отличная идея, то вы можете захотеть делать все слабо связанным. В целом это хорошая идея. В те моменты, когда вам нужно решить, как упаковать модули, слабое связывание предоставляет особенно полезные рекомендации.
Вам не приходится все абстрагировать и делать все подключаемым. В данном разделе я предоставлю вам некоторые средства, которые помогут вам принять решение о том, как моделировать ваши зависимости.
Стандартная библиот ека классов .NET (Base Class Library) состоит из множества сборок.
Каждый раз при написании кода, который использует тип из сборки стандартной библиотеки классов, вы добавляете в ваш модуль зависимость. В предыдущем разделе я рассуждал на тему того, как важно слабое связывание, и насколько програм мирование на
основании интерфейсов является основополагающим.
Означает ли это, что вы не можете ссылаться ни на одну из сборок стандартной библиотеки классов и использовать их типы напрямую в приложении? Что если вам захочется применить
XmlWriter
, который определен в сборке System .Xm l?
Вам не приходится обрабатывать все зависимости одинаково. Многие типы стандартной библиотеки классов можно использовать, не подвергая при этом опасности степень связанности приложения – но не все. Важно знать, как отличать типы, которые не представляют опасности, и типы, которые могут усилить степень связанности приложения. Сосредоточим наше внимание на последних.
Seams (Швы)
Везде, где мы решаем программировать на основании интерфейса, а не конкретного типа, мы вводим в приложение Seam. Seam – это место, где приложение монтируется из его составных частей, подобно тому, как куски материи сшиваются вместе с помощью швов.
Т акже это место, где мы можем распаковать приложение и работать с модулями изолированно.
Пример "Hello DI", который я создавал в разделе "Hello DI", содержит Seam между классами
Salutation и
ConsoleMessageWriter
, как это проиллюстрировано на рисунке 1-
12. Класс
Salutation не зависит напрямую от класса
ConsoleMessageWriter
; скорее, он использует интерфейс
IMessageWriter для записи сообщений. Вы можете разобрать приложение на части в месте этого Seam и смонтировать приложение заново с другими составителями сообщений.
Рисунок 1-12: Пример "Hello DI" из раздела "Hello DI" содержит Seam между классами
Salutation и
ConsoleMessageWriter
, потому что класс
Salutation выполняет запись только при помощи абстракции интерфейса
IMessageWriter

30
Как только вы изучите механизм внедрения зависимостей, он поможет вам классифициро ват ь ваши зависимости на стабильные и неустойчивые, но принятие решения о том, куда вставлять Seams вскоре станет для вас второстепенным. В следующих разделах будут более детально обсуждаться эти понятия.
Стабильные зависимости
Многие модули в стандартной библиотеке классов и за ее пределами не представляют никакой угрозы для степени модульности приложения. Они содержат функциональность, допускающую повторное использование, которую вы можете использовать для того, чтобы сделать ваш код более кратким.
Модули стандартной библиотеки классов всегда доступны в рамках вашего приложения, поскольку для его запуска необходим .NET Fram ework. Концепция параллельной разработки не применима к этим модулям, поскольку они уже существуют, и вы всегда можете повторно использовать стандартную библиотеку классов в других приложениях.
По умолчанию вы можете считать, что большинство (но не все) типов, определенных в
BCL в качестве безопасных или стабильных зависимостей – я называю их ст абильными, потому что они уже присутствуют там, склонны к обратной совместимости, а их вызов имеет детерминированные последствия.
Большинство стабильных зависимостей являются типами BCL, но и другие зависимости также могут быть стабильными. Важными критериями стабильных зависимостей являются перечисленные ниже критерии:

Класс или модуль уже существует.

Вы считаете, что новые версии не будут содержать разрушительных изменений.

Рассматриваемые типы содержат детерминированные алгоритмы.

Вы никогда не думаете о том, что вам придется заменять класс или модуль на что- то другое.
Как ни странно, DI-контейнеры сами по себе будут проявлять себя как стабильные зависимости, потому что они удовлетворяют всем критериям. Когда вы решаете взять за основу вашего приложения определенный DI-контейнер, вы рискуете спотыкаться об этот выбор на протяжении всего жизненного цикла приложения; тем не менее, это еще одна причина того, почему вы должны ограничить использование контейнера в C omposition
Root.

31
Другие примеры приложений могут включать в себя специализированные библиотеки, которые инкапсулируют алгоритмы, имеющие отношение к вашему приложению. Если вы разрабатываете приложение, имеющее дело с химией, вы можете ссылаться на стороннюю библиотеку, которая содержит химико-специфич ескую функциональность.
Обычно зависимости можно считать стабильными путем исключения: они стабильны, если они не являются неустойчивыми.
Неустойчивые зависимости
Введение в приложение Seam s является дополнительно й работой, поэтому вам следует это делать только при необходимости. Может существовать более одной причины необходимости изоляции зависимости за пределами Seam , но они тесно связаны с преимуществами слабого связывания, которое обсуждалось в разделе "Преимущества DI".
Т акие зависимости можно узнать по их склонности создавать препятствия для одного или большинства из этих преимуществ. Они не стабильны, потому что они не обеспечивают достаточное основание для приложений, и по этой причине я называю их неустойчивыми зависимостями. Зависимость следует считать неуст ойчивой, если соблюдается любой из следующих критериев:

Зависимость вводит требование, согласно которому необходимо установить и сконфигурировать исполняющую среду для приложения. Реляционная база данных является архитипическим примером: если мы не скроем реляционную базу данных за Seam, мы никогда не сможем заменить ее на какую-либо другую технологию.
Это требование также усложняет процесс установки и запуска автоматизированных модульных тестов.
Базы данных – это отличный пример типов BCL, которые являются неустойчивыми зависимостями: даже если LINQ to Entities – это технология, которая содержится в BCL, ее использование подразумевает реляционную базу данных.
Другие необрабатываемые ресурсы такие, как очереди сообщений, веб-сервисы и даже файловые системы также попадают в эту категорию. Пожалуйста, отметьте, что это не сами конкретные .NET типы являются неустойчивыми, но скорее то, что они подразумевают об исполняющей среде.
Признаками этого вида зависимостей является отсутствие слабого связывания и расширяемости, а также невозможность тестирования.

Зависимость еще не существует, но все еще разрабатывается. Очевидным признаком таких зависимостей является неспособность выполнять параллельну ю разработку.

Зависимость не установлена на всех машинах организации, занимающейся разработкой. Это может быть аргументом в пользу дорогих сторонних библиотек или зависимостей, которые не могут устанавливаться на все операционные системы. Наиболее универсальный признак – невозможнос ть тестирования.

Зависимость содержит недетерминированное поведение. Это чрезвычайно важно в модульных тестах, поскольку все тесты должны быть детерминированными.
Т ипичными источниками недетерминированности являются случайные числа и алгоритмы, которые зависят от текущих даты и времени.

32
Заметьте, что такие универсальные источники недетерминированности, как
System.Random
,
System.Security.Cryptography.RandomNumberGenerator или
System.DateTime.Now определены в mscorlib, поэтому вам не избежать ссылки на сборку, в которой они определены. Т ем не менее, вам следует относиться к ним, как к неустойчивым зависимостям, потому что они склонны к разрушению тестируемости.
Неустойчивые зависимости являются центральным вопросом механизма внедрения зависимостей. Мы вводим Seams в приложение скорее для неустойчивых зависимостей, нежели для стабильных. Кроме того, они обязывают нас компоновать их при помощи механизма DI.
Т еперь, когда вы поняли разницу между стабильными и неустойчивыми зависимостями, вы можете приступить к рассмотрению горизонтов области применения механизма внедрения зависимостей. Слабое связывание – это доминирующий принцип проектирования, поэтому механизм DI (как инструмент его реализации) должен быть в вашем коде повсюду. Не существует четкой границы между предметом механизма внедрения зависимостей и проектированием добротного программного обеспечения, но для того чтобы определить контекст остальной части книги, я быстро опишу, что какие вопросы охватывает механизм DI.

33 1.4. Область применения DI
Как мы видели в разделе "Hello DI", важный элемент механизма внедрения зависимостей
– это вынесение различных ответственностей в отдельные классы. Одна из ответственностей, которую мы выносим в классы – это задача создания экземпляров зависимостей.
Как только класс уступает контроль над зависимостями, он отказывается от решения выбирать конкретные реализации. Т ем не менее, как разработчики, мы приобретаем некоторые преимущества.
П римечание
Как разработчики мы приобретаем контроль путем удаления этого контроля из классов, которые применяют зависимости. Это применение принципа единст венной
от ветственност и: эти классы должны иметь дело только с их заданной областью ответственности, не беспокоясь при этом о том, как создаются зависимости.
Поначалу может показаться недостатком позволять классу отказываться от контроля над тем, какие объекты создаются, но, как разработчики, мы не теряем этот контроль – мы всего лишь переносим его в другое место.
Т ем не менее, композиция объектов (object com position) является не единственным пластом управления, который мы удаляем, поскольку класс также теряет способность контролировать время жизни объекта. Когда в класс внедряется экземпляр зависимости, потребитель не знает, когда он был создан или когда он выйдет из области видимости. Во многих случаях это не представляет интереса для потребителя, но в некоторых случаях это важно.
Механизм внедрения зависимостей дает нам возможность управлять зависимостями универсальным способом. Когда пользователи напрямую создают и настраивают экземпляр ы зависимостей, каждый из них может делать это своим собственным способом, который может противоречить тому, как это делают остальные. Не существует способа, с помощью которого мы могли бы централизованно управлять зависимостями, а также способа, при помощи которого мы бы с легкостью обращались к сквозным концепциям.
Благодаря механизму DI мы приобретаем возможность перехватывать каждый экземпляр зависимости и влиять на него до того, как он будет передан пользователю.
Благодаря механизму внедрения зависимостей мы можем компоновать приложения во время перехватывания зависимостей и контролирования их жизненного цикла.
Композиция объектов, механизм перехвата и управление жизненным циклом – это три аспекта механизма внедрения зависимостей. Далее я вкратце раскрою эти аспекты; более детальное описание приводится в части 3 этой книги.
Композиция объектов
Чтобы воспользоваться результатами таких преимуществ, как расширяемость, позднее связывание и параллельная разработка, мы должны уметь компоновать классы в приложениях (см. рисунок 1-13). Такая композиция объектов часто является самой главной мотивацией для введения в приложение механизма внедрения зависимостей.
Первоначально механизм DI был синонимичен композиции объектов; это единственный

34 аспект, обсуждаемый в оригинальной статье Мартина Фаулера относительно этого вопроса.
Рисунок 1-13: Композиция объектов означает, что модули в приложениях можно компоновать.
Существует несколько способов, с помощью которых мы можем компоновать классы в приложении. Когда я рассматривал позднее связывание, я использовал конфигурационный файл и несколько реализаций динамических объектов для того, чтобы вручную скомпоновать приложение из доступных модулей, но я также мог использовать и
технологию конфигурирования в коде или DI-контейнер. Мы вернемся к этим вопросам в главе 7.
Несмотря на то, что первоначальное значение механизма внедрения зависимостей тесно связано с композицией объектов, остальные аспекты также оказались уместными.
Жизненный цикл объектов
Класс, который отдает контроль над своими зависимостями, отказывается не просто от возможности выбирать определенные реализации абстракций. Он также отказывается и от возможности контроля над тем, когда создаются экземпляр ы зависимостей, и когда они выходят за рамки области применения механизма DI.
В .NET о большинстве из этих вещей заботится за нас G arbage Collector. Пользователь может иметь внедренные в класс зависимости и использовать их настолько долго, насколько того требует класс. Когда это происходит, зависимости выходят за рамки области применения механизма DI. Если на эти зависимости не ссылается больше ни один класс, то они как раз подходят для "сборки мусора" (garbage collection).
Что если два пользователя используют один и тот же вид зависимостей? Рисунок 1-14 иллюстрирует тот факт, что мы можем выбрать вариант внедрения отдельного экземпляра для каждого пользователя, тогда как рисунок 1-15 демонстрирует, что мы можем наоборот выбрать вариант, при котором единственный экземпляр будет совместно использоваться несколькими пользователями. Т ем не менее, с точки зрения пользователей между этими вариантами нет никакой разницы. Согласно принципу замещения Лисков пользователь должен одинаково воспринимать все экземпляры данного интерфейса.
Рисунок 1-14: Для каждого из пользователей, совместно использующих один и тот же тип зависимостей, внедряется свой собственный приватный экземпляр.

35
Рисунок 1-15: индивидуа льных пользователей, совместно использующих один и тот же тип зависимостей, внедряется один и тот же экземпляр.
Поскольку зависимости могут использоваться несколькими пользователями одновременно, единичный пользователь, вероятно, не может управлять их жизненным циклом. Поскольку управляемый объект может выходить за рамки области применения механизма DI и может быть удален сборщиком мусора (garbage collector), это не слишком важно, но когда зависимости реализуют интерфейс
IDisposable
, все становится намного сложнее.
В целом управление жизненным циклом является отдельным аспектом механизма внедрения зависимостей и настолько важным, что я выделил для него всю главу 8.
Отказ от контроля над зависимостью также означает отказ от контроля над ее жизненным циклом; нечто, находящееся выше в стеке вызовов, должно управлять жизненным циклом зависимости.
Перехват
Когда мы делегируем контроль над зависимостями стороннему компоненту, как это демонстрирует рисунок 1-16, мы также приобретаем возможность модифицировать эти зависимости до того, как мы передадим их в классы, которые используют эти зависимости.
Рисунок 1-16: Вместо того, чтобы внедрять первоначально запланированные зависимости, мы можем модифицировать эти зависимости посредством упаковывания их в другой класс до того, как передать их пользователю. Пунктирная стрелка указывает на направление действия – направление зависимости идет в обратном направлении.

36
В примере "Hello DI" я первоначально внедрял экземпляр
ConsoleMessageWriter в класс
Salutation
. Затем, модифицируя пример, я добавил возможность обеспечения безопасности путем создания нового
SecureMessageWriter
, который всего лишь делегирует дальнейшу ю работу
ConsoleMessageWriter при аутентификации пользователя. Это позволяет нам поддерживать принцип единственной от ветст венност и.
Это возможно сделать, потому что мы всегда программируем на основании интерфейсов; помните, что эти зависимости всегда должны быть абстракциями. Что касается класса
Salutation
, то он не заботится о том, является ли используемый
IMessageWriter экземпляром
ConsoleMessageWriter или
SecureMessageWriter
SecureMessageWriter может упаковывать
ConsoleMessageWriter
, который, однако, выполняет реальную работу.
Примечание
Перехват – это применение паттерна проектирования Decorator. Не переживайте, если вам не знаком паттерн проектирования Decorator – в главе 9 я предоставлю вам повторный курс, который всецело посвящен механизму перехвата.
Такие возможности перехвата переносят нас прямиком к аспектно-ориентированному
программированию (Aspect-Oriented Program ming) – тесно связанная с механизмом внедрения зависимостей тема, которая, тем не менее, выходит за пределы этой книги.
Благодаря механизму перехвата мы можем применять такие сквозные механизмы, как авторизация, контрольные проверки, управление доступом, валидация и т.д. в хорошо структурированной манере, которая позволяет нам поддерживать концепцию разделения.
Механизм DI с точки зрения трех аспектов
Несмотря на то, что механизм внедрения зависимостей был задуман как набор паттернов, целью которых было решение проблемы композиции объектов, этот термин был в дальнейшем расширен и теперь также охватывает такие понятия, как жизненный цикл объектов и механизм перехвата. На сегодняшний момент я считаю, что механизм DI логично заключает в себе все три аспекта.
Композиция объектов доминирует среди этих трех аспектов, поскольку без гибкой композиции объектов не было бы ни механизма перехвата, ни необходимости управлять жизненным циклом объектов. Композиция объектов доминировала на протяжении

37 большей части этой главы и будет продолжать доминировать на протяжении всей оставшейся книги, но мы не должны забывать об остальных аспектах. Композиция объектов предоставляет фундамент, а механизм управления жизненным циклом обращается к некоторым важным побочным эффектам, но пожинать плоды преимуществ механизма внедрения зависимостей мы начинаем, главным образом, когда переходим к механизму перехвата.
В части 3 я посвятил каждую главу отдельному аспекту, но в данной главе я предоставил обзор этих аспектов, поскольку важно знать, что на практике механизм внедрения зависимостей – это больше, чем просто композиция объектов.

38 1.5. Резюме
Механизм внедрения зависимостей по своей природе является средством достижения результата, а не целью. Это самый лучший способ разрешения слабого связывания, важной составляющей поддерживаемого кода. Преимущества, которые мы можем заполучить благодаря слабому связыванию, не всегда тотчас же очевидны, но становятся заметными с течением времени, когда возрастает сложность кода. Сильно связанный код, в конце концов, выродится в "спагетти-код" (Spaghetti Code), между тем, как хорошо сконструированный, слабо связанный код может оставаться поддерживаемым. Для того чтобы достичь поистине "гибкого проектирования" (Supply Design), нужно не просто слабое связывание, но программирование на основании интерфейса является необходимым условием "гибкого проектирования".
Механизм DI – это больше, чем просто набор принципов и паттернов. Это, скорее, способ придумывания и конструирования кода, нежели средства и приемы – важный пункт слабого связывания, это и есть механизм внедрения зависимостей, и для эффективности он должен присутствовать в вашем коде повсюду.
П одсказка
Механизм внедрения зависимостей должен быть доминирующим. Вы не можете с легкостью подстраивать слабое связывание под существующий код.
Существует множество моментов неправильного понимания механизма DI. Некоторые люди думают, что он касается только таких узких проблем, как позднее связывание или модульное тестирование; несмотря на то, что эти аспекты проектирования программного обеспечения, безусловно, извлекают пользу из механизма внедрения зависимостей, область его применения намного шире. Абсолютной целью механизма внедрения зависимостей является удобство сопровождения кода.
В начале главы я утверждал, что вы должны забыть все, что вы знали до этого о механизме внедрения зависимостей. Это остается справедливым и для всей оставшейся части книги: вы должны очистить свой разум. В потрясающей публикации блога Николас
Блумхардт пишет:
Слов арь и ассоциатив ный м ассив – это одни из перв ых конструкций, о которых м ы узнаем при разработке
программ ного обеспечения. Легко увидеть аналогию между слов арем и IoC-контейнером, который
компонует объекты посредств ом испол ьзов ания м еханизма внедрения зав исимостей.
Понимание механизма внедрения зависимостей как сервиса, смоделированного на основании строк словаря, напрямую ведет к анти-паттерну Service Locator. Вот поэтому я так много акцентировал ваше внимание на необходимости очистить ваш разум даже от самых основных предположений. В конце концов, говоря о словарях, мы говорим о некой субстанции, которая принадлежит к "раболепному разуму программирования".
Цель механизма DI – сделать код поддерживаемым. Небольшие по размеру базы кода, подобные примеру "Hello World", являются по существу поддерживаемыми благодаря своему размеру; вот поэтому механизм внедрения зависимостей воспринимается в простых примерах как лишняя разработка. Чем больше по размеру становится база кода, тем более видимыми становятся преимущества механизма внедрения зависимостей. Я

39 посвятил следующу ю главу более значительному и сложному примеру для того, чтобы продемонстрироват ь эти преимущества.

40 2. Комплексный пример
Меню:

Комплексный пример

Как не стоит поступать

Как действовать правильно
Сообщение о том, что беарнский соус – это "эмульсионный соус, приготовленный из яичного желтка и масла", не привьет вам способность готовить этот соус. Самый лучший способ обучения – это практика; пример часто может преодолеть разрыв между теорией и практикой. Полезно понаблюдать за тем, как профессиональный повар готовит беарнский соус, перед тем, как попытаться приготовить его самостоятельно.
Когда я знакомил вас в предыдущей главе с механизмом внедрения зависимостей, я предоставил вам высококлассный обзор для того, чтобы вы поняли его цель и основные принципы. Т ем не менее, с помощью этого примера нельзя по достоинству оценить DI.
Механизм внедрения зависимостей – это способ разрешения слабого связывания, а слабое связывание – это первый и самый главный эффективный способ справиться со сложностью.
Большинство программного обеспечения является сложным в том плане, что ему нужно обращаться ко многим сущностям одновременно. Помимо бизнес-сущностей (которые сами по себе могут быть сложными), программное обеспечение должно также обращаться к сущностям, связанным с вопросами обеспечения безопасности, диагностики, операций и расширяемости. Вместо того обращения ко всем этим сущностям в одном большом комке, слабое связывание позволяет нам обращаться к каждой сущности по отдельности. Проще обращаться к каждой сущности изолированно – но, в конечном счете, мы все еще должны компоновать этот сложный набор сущностей. Давайте рассмотрим сложный пример для того, чтобы лучше продемонстрировать механизм внедрения зависимостей.
П редупреждение
Весь смысл слабо связанного кода – борьба со сложностью эффективным способом, и нам необходимы комплексные примеры для того, чтобы проиллюстрироват ь сложные сущности. Должно быть, вы предполагаете, что большинство примеров в этой книге являются сложными, и в них входят составные классы из составных библиотек.
Сложность – это часть игры.
Я посвятил всю эту главу сложному примеру. Думаю, что это важно – сравнить слабо связанный код с более "традиционным", сильно связанным примером, поэтому в этой главе вы найдете одну и ту же возможность, реализованную двумя способами. Сначала я подготавливаю почву, демонстрируя, как легко писать сильно связанный код. Затем я реализую ту же функциональность при помощи механизма внедрения зависимостей. Вы можете пропустить пример сильно связанного кода, если хотите прямо сейчас увидеть некоторый слабо связанный код. После прочтения этой главы вы должны уже начать понимать, как можно использовать DI для того, чтобы создавать слабо связанный код.
Рисунок 2-1: Эта глава содержит две разновидности одного и того же примера. Сначала вы увидите, как легко писать сильно связанный код. Затем вы увидите, как писать то же самое приложение при помощи слабо связанного кода. Оба примера содержат как сам

41 пример, так и его анализ. Если вы хотите прямо сейчас увидеть слабо связанный код, то вы можете пропустить первый раздел.
2.1. Как не стоит поступать
2.2. Как действовать правильно
2.3. Расширение шаблонного приложения
2.4. Резюме

42 2.1. Как не стоит поступать
Сама идея создания слабо связанного кода не является чрезвычайно противоречивой, но существует огромный разрыв между намерением и практикой. Перед тем как показать вам, как использовать механизм внедрения зависимостей для создания слабо связанных приложений, я хочу продемонстрировать вам, как легко все может пойти неправильным путем.
Общепринятой попыткой использования слабо связанного кода является создание многоуровневого приложения. Всякий может нарисовать схему трехуровневого приложения, и рисунок 2-2 доказывает, что я тоже могу это сделать.
Рисунок 2-2: Стандартная архитектура трехуровневого приложения. Это самая простая общепринятая разновидность архитектуры n-уровневого приложения, где приложение состоит из n-уровней, каждый из которых состоит из одного или более одного модуля.
Некоторые разновидности n-уровневых схем будут иметь вертикальные блоки, которые обозначают уровни составного приложения. Т акие схемы часто используются для представления таких сквозных сущностей, как обеспечение безопасности и вход в систему.
Создание трехуровневой схемы является обманчиво простым процессом, но само рисование схемы похоже на утверждение о том, что у вас будет беарнский соус с собственным стейком: это объявление намерения, которое не дает никаких гарантий относительно окончательного результата. У вас может получиться и что-то другое, как вы сами это скоро увидите.
Создание сильно связанного приложения
Существует более одного способа просмотра и проектирования гибкого и поддерживаемого сложного приложения, но архитектура n-уровневого приложения основывается на хорошо известном, практически опробованном подходе. Проблема в том, чтобы реализовать его корректно.
Вооружившись трехуровневой схемой, подобной той, что продемонстрирована на рисунке
2-2, вы можете приступить к созданию приложения.

43
Знакомство с Мэри Роуэн
Мэри Роуэн – профессиональный .NET разработчик, работающая на местного сертифицированного партнера компании Microsoft , который, в основном, занимается разработкой веб-приложений. Ей 34 и с программным обеспечением она работает уже 11 лет. Это делает ее одним из самых опытных разработчиков компании, и она часто выступает в роли наставника для младших разработчиков в дополнение к выполнению ее регулярных обязанностей в качестве старшего разработчика.
В целом Мэри получает удовольствие от своей работы, но ее расстраивает то, что основные этапы работы зачастую пропускаются, и это вынуждает ее и ее коллег работать в течение многих часов и недель, чтобы успеть выполнить все в срок. Она полагает, что должны существовать более эффективные способы создания программного обеспечения.
Стремясь узнать больше информации об эффективности, она покупает множество книг по программированию – но у нее редко есть время на их прочтение.
Большинство своего свободного времени она тратит на мужа и двух дочерей. Мэри любит заниматься восхождением в горы. К тому же она увлеченный повар и уж точно знает, как приготовить настоящий беарнский соус.
Мэри попросили создать новое приложение электронной коммерции на платформе
ASP.NET MVC и Entity Framework с SQL Server, используемым в качестве хранилища данных. Для того чтобы максимально увеличить модульность, это приложение должно быть трехуровневым.
Первой реализованной возможностью должен быть простой список рекомендуемых товаров, который вытаскивается из таблицы базы данных и отображается на веб-странице; пример продемонстрирован на рисунке 2-3. Если список просматривает привилегированный пользователь, то цена всех товаров должна быть снижена на 5 процентов.
Рисунок 2-3: Скриншот веб-приложения электронной коммерции, которое попросили создать Мэри. На нем изображен простой список рекомендуемых товаров и их цен ("kr." – это буквенный символ валюты для датских крон)

44
Давайте подсмотрим, как Мэри реализует первую возможность приложения.
Уровень данны х
Поскольку Мэри нужно вытаскивать данные из таблицы базы данных, она решила начать с реализации уровня данных. Первый шаг – определить саму таблицу базы данных. Для создания таблицы, продемонстрированно й на рисунке 2-4, Мэри использует SQL Server
Managem ent Studio.
Рисунок 2-4: Мэри создает таблицу Product при помощи SQL Server Management Studio; альтернативные подходы включают написание T-SQL скрипта, или создание таблицы с помощью Visual Studio или с помощью каких-либо других средств
Для реализации уровня "Data Access Layer" (уровень доступа к данным) Мэри добавляет в свое решение новую библиотеку. В Visual Studio она использует мастер Ent ity Data M odel
W izard для того, чтобы сгенерировать целостную модель сущностей из базы данных, которую она ранее создала. Чтобы завершить оформление модели, она изменяет несколько названий, как это показано на рисунке 2-5.
Рисунок 2-5: Сущность
Product
, которая сгенерирована из таблицы базы данных
Product
, продемонстрированной на рисунке 2-4. Мэри изменила название столбца
Featured на
IsFeatured
, а также изменила несколько названий в сгенерированном
ObjectContext
(не продемонстрировано).

45
Примечание
Не стоит волноваться, если вы не знакомы с Microsoft Entity Framework. Подробная информация о реализации доступа к данным в данном контексте не является важной, поэтому вам нужно всего лишь суметь последовать данному примеру, даже если вы более знакомы с другой технологией доступа к данным.
Сгенерированные сущности
ObjectContext и
Product
– это открытые типы, которые находятся в пределах одной и той же сборки. Мэри знает, что в дальнейшем ей нужно будет добавить в приложение больше возможностей, но добавление компонента доступа к данным, который необходим для реализации первой возможности, на этом завершено.
Рисунок 2-6 демонстрирует, как далеко Мэри зашла в реализации многоуровневой архитектуры, которая была представлена на рисунке 2-2.
Рисунок 2-6: До настоящего времени Мэри реализовывала уровень "Data Access Layer"
(уровень доступа к данным) своего приложения. Уровни "Dom ain Logic Layer" (уровень доменной логики) и "User Interface Layer" (уровень пользовательского интерфейса) отодвинуты на второй план до тех пор, пока не будет реализована первая возможность.
Теперь, когда уровень доступа к данным реализован, следующий логичный шаг – реализация уровня доменной логики.
Доменный уровень
При отсутствии какой-либо доменной логики список товаров, предоставляемых с помощью сгенерированного
ObjectContext
, формально мог использоваться напрямую на уровне пользовательского интерфейса.
Предупрежде ние
За исключением простых приложений, содержащих отчеты, в приложениях всегда присутствует доменная логика. Сначала вы можете и не понять ее, но как только вы начнете узнавать предметную область приложения, его вложенные и скрытые правила и предположения постепенно всплывут. Реализация данной логики на уровне пользовательского интерфейса или на уровне доступа к данным приведет к печальным последствиям. Окажите себе честь и создайте сначала уровень доменной логики.

46
В требованиях к приложению Мэри говорится, что для привилегированных пользователей должен отображаться список цен с пятипроцентной скидкой. Мэри еще не поняла, как идентифицировать привилегированного пользователя, поэтому она попросила совета у своего коллеги Дженса:
Мэри: Мне нужно реализоват ь эту бизнес-логику так, чт обы привилегированный
пользователь получал пят ипроцент ную скидку.
Дженс: Звучит легко. Просто умножь на 0,95.
Мэри: Спасибо, но я не об эт ом хот ела тебя спросит ь. Я хот ела спросит ь, как мне
следует идентифицироват ь привилегированного пользователя?
Дженс: Понятно. У т ебя веб-приложение или настольное приложение?
Мэри: Веб-приложение.
Дженс: Ясно, т огда т ы можеш ь определит ь профиль пользоват еля и использоват ь
свойст во
IsPreferredCustomer
. Ты можешь получит ь профиль через
HttpContext
.
Мэри: Прит орм ози-ка, Дженс. Этот код должен быт ь на уровне доменной логики. Это
библиот ека. Нет никакого
HttpContext
.
Дженс: О… (некот орое время думает ). Я все еще счит аю, что тебе нужно использоват ь
возможност ь Profile ASP.NET для поиска значения по пользоват елю. Ты можешь потом
передат ь значение в т вою доменную логику как булево.
Мэри: Я не знаю…
Дженс: Это ещ е и даст т ебе уверенность в т ом, чт о т ы следуешь концепции
разделения, пот ому чт о доменной логике не приходит ся иметь дело с обеспечением
безопасност и. Сама знаеш ь: Принцип единичной от ветственност и! Это самый
быст рый способ дост ижения эт ого!
Мэри: Думаю, т ы прав.
П редупреждение
Дженс базировал свой совет на своем техническом знании ASP.NET . Как только обсуждение вывело его из его же комфортной зоны, он стал отбиваться от Мэри при помощи тройной комбинации умных словечек. Знайте, что он не знает, о чем говорит: он неправильно использует концепцию разделения понятий, абсолютно неправильно использует принцип единичной ответственности, и он упоминает о том, что это самый быстрый способ, только потому что недавно слышал, как кто-то еще с энтузиазмом говорил об этом.
Вооружившись советом Дженса, Мэри создает новый проект библиотеки C# и добавляет класс с названием
ProductService
, который продемонстрирован в листинге ниже. Для того чтобы сделать класс
ProductService компилируемым, она должна добавить ссылку на свою библиотеку Data Access, поскольку там определен класс
CommerceObjectContext

47
Листинг 2-1: Класс
ProductService
, добавленный Мэри public partial class ProductService
{ private readonly CommerceObjectContext objectContext; public ProductService()
{ this.objectContext = new CommerceObjectContext();
} public IEnumerable
GetFeaturedProducts(bool isCustomerPreferred)
{ var discount = isCustomerPreferred ? .95m : 1; var products = (from p in this.objectContext.Products where p.IsFeatured select p).AsEnumerable(); return from p in products select new Product
{
ProductId = p.ProductId,
Name = p.Name,
Description = p.Description,
IsFeatured = p.IsFeatured,
UnitPrice = p.UnitPrice * discount
};
}
}
Мэри счастлива, что смогла инкапсулировать технологию доступа к данным (LINQ to
Ent ities), конфигурацию и доменную логику в классе
ProductService
. Она делегировала знания о пользователе вызывающему оператору путем передачи параметра в isCustomerPreferred
, и использует это значение для расчета скидки для всех товаров.
Дальнейшая обработка могла бы включать замену жестко-зако дированного значения скидки (
0,95
) на настраиваемое число, но на данный момент такой реализации достаточно. Мэри почти все сделала – остался только пользовательс кий интерфейс. Мэри решила, что это может подождать до следующего дня.
Рисунок 2-7 демонстрирует, как далеко Мэри зашла в реализации архитектуры, представленной на рисунке 2-2.
Рисунок 2-7: На данном этапе Мэри реализовала уровень доступа к данным и уровень доменной логики. Если сравнивать с рисунком 2-6, то здесь добавлен уровень доменной логики. Уровень пользовательского интерфейса все еще остается не реализованным

48
После реализации уровня доступа к данным и уровня доменной логики остается реализовать только уровень пользовательского интерфейса.
Уровень пользовательского интерфейса
На следующий день Мэри возобновляет свою работу над приложением электронной коммерции, добавляя новое ASP.NET MVC приложение в свое решение.
П римечание
Не стоит волноваться, если вы не знакомы с фреймворком ASP.NET MVC. Запутанные детали того, как функционирует MVC framework, не рассматриваются в этой книге.
Важной составляющей является то, как используются зависимости, а это относительно нейтральная тема.
Ускоренный курс по ASP.NET MVC
ASP.NET MVC берет свое название от паттерна проектирования Model View Cont roller. В данном контексте самое важное, что необходимо понять, – это то, что, когда поступает веб-запрос, контроллер управляет этим запросом, потенциально используя модель
(доменную) для того, чтобы затронуть этот запрос и сформировать ответ, который, в конце концов, отображается с помощью представления.
Контроллер – это обычно класс, который наследуется от абстрактного класса
Controller
У него есть один или более одного метода действия, которые управляют запросами, например. Класс
HomeController обладает методом
Index
, который управляет запросом страницы, отображаемой по умолчанию.
При возврате метода действия он передает итоговую модель в представление посредством экземпляра
ViewResult
Следующий листинг демонстрирует, как Мэри реализует метод
Index класса
HomeController для того, чтобы извлечь обработанные товары из базы данных и передать их в представление. Для того чтобы сделать этот код компилируемым, ей нужно добавить ссылки как на библиотеку Data Access, так и на библиотеку Dom ain, поскольку класс
ProductService определен в библиотеке Domain, а класс
Product определен в библиотеке
Data Access.
Листинг 2-2: Метод
Index в используемом по умолчанию классе контроллера public ViewResult Index()
{ bool isPreferredCustomer = this.User.IsInRole("PreferredCustomer"); var service = new ProductService(); var products = service.GetFeaturedProducts(isPreferredCustomer); this.ViewData["Products"] = products; return this.View();
}
Являясь частью жизненного цикла ASP.NET MVC, свойство
User класса
HomeController автоматически заполняется корректным объектом пользователя, поэтому М эри использует его для того, чтобы определить, является ли текущий пользователь привилегированным.

49
Вооружившись этой информацией, она может вызвать доменную логику, чтобы получить список рекомендуемых товаров. Через некоторое время я вернусь к этому моменту, потому что здесь есть ловушка, но сейчас я позволю Мэри обнаружить эту ошибку самостоятельно.
Список товаров должен отображаться с помощью представления
Index
. Следующий листинг демонстрирует разметку для этого представления.
Листинг 2-3: Разметка представления
Index
1.
1   2   3   4   5   6   7   8   9   ...   43


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