четверг, 7 февраля 2013 г.

О книге Марка Симана “Dependency Injection in .NET”

clip_image002

Открывая книгу по «новомодной технике проектирования», такой как инверсия зависимостей ожидаешь увидеть описание очередной серебряной пули. Дескать, вот, до этого все мы жили неправильно, а теперь, вооружившись ломом контейнером и какой-то матерью мы сможем писать новые приложения за 10 минут.

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

ЦИТАТА
Если вы думаете, что DI требует использование DI контейнера, то это еще один момент, который нужно переучивать. DI – это набор принципов и паттернов, и DI контейнер является хотя и полезным, но необязательным инструментом.

Глядя на типовое использование инверсии зависимостей во многих проектах складывается впечатления, что DI противоречит основным принципам проектирования, о которых так много пишут Мейер и Буч.

В угоду якобы «слабосвязанного дизайна» выделяются десятки ненужных интерфейсов, направо и налево используются сами контейнеры в роли Service Locator-ов, создаются классы с десятком параметров конструктора, обязательные зависимости классов передаются через свойства и т.д. Глядя на все это складывается впечатление, что DI-контейнеры противоречат таким вещам, как инварианты класса и четкий контракт между классом и его клиентами.

ЦИТАТА
DI – это не самоцель. Управление зависимостями ведет к слабой связанности (loose coupling), что ведет к более сопровождаемому коду.

Марк начинает с того, что старается развеять основные мифы о DI, и лишь потом начинает описание основных концепций. Так, автор с первых страниц говорит о том, что Service Locator является анти-паттерном и контейнер должен использоваться лишь в одном месте, в «корне приложения» (в так называемом Composition Root), говорит о том, что IOC != DI, пишет о проблемах с использованием файлов конфигурации для управления содержимым контейнера и многом другом.

ЦИТАТА
Не ждите, что использование DI контейнеров волшебным образом сделает ваш код слабосвязанным. DI контейнер может сделать управление зависимостями более эффективным, но ваше приложение должно быть прежде всего спроектировано с учетом DI техник и принципов.

Очень порадовало то, что главной целью книги автор поставил не рассмотрение конкретных библиотек, а сделал акцент на общих принципах управления зависимостями и показал связь этих принципов со стандартными паттернами проектирования и ОО принципами (ведь большая часть классических паттернов проектирования используют управление зависимостями в том или ином виде: так, декоратор использует Constructor Injection, а стратегия – Method Injection или Constructor Injection и т.д.).

На протяжении первых трех частей вообще не используются никакие контейнеры и во всех примерах используются «самопальное» управление зависимостями (так называемый Poor Man’s DI).

ЦИТАТА
DI-контейнер – это лишь инструмент, и как любой инструмент, его можно использовать правильно, а можно и неправильно.

Очень порадовало, что помимо самих принципов и паттернов автор описывает и то, когда они применимы, а когда нет. У любого паттерна есть своя область применения, один из них подходит в одном случае и не подходит в другом. А некоторые паттерны вообще кажутся весьма привлекательными, а на деле оказывается, что их использование может привести к серьезным проблемам. Именно по этой причине целая часть книги посвящена основным DI-паттернам и анти-паттернам; и хотя это не гарантирует их правильного применения в наших проектах, это дает достаточно пищи для размышлений, и делает шансы на использование этого инструмента корректным образом максимально большими.

ЦИТАТА
Чрезмерное количество параметров конструктора (Constructor Over-injection) не является проблемой управления зависимостей вообще, и передачи зависимостей через конструктор (Constructor Injection), в частности. Скорее, это показатель того, что у рассматриваемого класса слишком много ответственностей. Этот душок (code smell) идет от класса, а не от передачи зависимостей через конструктор; и мы, как обычно, должны использовать его, как стимул для улучшения нашего кода.

Если посмотреть на разные DI контейнеры, то можно обратить внимание, что одни и те же концепции во многих местах называются по разному. Любой контейнер поддерживает определенный набор стратегий управления временем жизни зависимости (lifetime management), и хотя многие из них аналогичны очень часто они называются по разному.

В этом плане, книга решает две дополнительные задачи. Во-первых, автор вводит согласованную терминологию (Auto Wiring, Composition Root, Bastard Injection etc), а во-вторых, дает знания основных принципов, которые можно использовать с любым контейнером или без него. Эти знания можно спокойно использовать повторно, что сэкономит время на изучение новых библиотек, а также позволит увидеть картину управления зависимостями целиком.

ЦИТАТА
Добавление Швов в приложение требует дополнительных усилий, поэтому добавлять их стоит только в случае необходимости.
(Марк о выделении интерфейсов и создании дополнительных швов приложения)

У этой книги есть один недостаток – это последняя четвертая часть, посвященная описанию шести DI-контейнеров. У меня сложилось впечатление, что автор посчитал, что книга из 350 страниц выглядит не солидно, либо же главным KPI для него было именно количество страниц.

В любом случае, КПД последних 200 страниц крайне мало. Первая проблема заключается в том, что каждый контейнер является достаточно серьезным монстром, и его невозможно описать на 50 страницах. Во-вторых, автор сделал разумную попытку унифицированного описания каждого контейнера, что привело к большому количеству «копи-пасты» (вплоть до одинаковых абзацев), и отсутствию описания уникальных возможностей контейнеров.

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

Подводя итог, хочу сказать, что “Dependency Injection in .NET” является наиболее полным и последовательным источником информации по управлению зависимостями. Я бы рекомендовал первые 3 части этой книги к обязательному прочтению всем командам, которые используют DI контейнеры в своих проектах.

Оценка: 5+ (для частей 1-3) и 2 (для части 4).

13 комментариев:

  1. У меня не сложилось впечатление, что часть с описаниями контейнеров - хуже. Она - для другого. Первая часть - некое теоретическое обоснование - что такое хорошо и как с ним живут. Вторая часть - сугубо практическая. Есть цель - выбрать контейнер для проекта. Ага, смотрим, как умеет этот, как умеет этот. И вуаля - выбор сделан. Может быть стоило разделить на две книги, но тогда первая была бы слишком философская, а вторая - слишком примитивная. В общем, хорошая книга на все случаи :)

    ОтветитьУдалить
  2. Ну, мне не показалось, что того описания достаточно, чтобы сделать вывод о выборе контейнера. Слишком уж все под одну гребенку заточено. В этом и проблема: после прочтения книги мне не нужна эта глава, документации, которая идет с контейнером вполне достаточно.
    З.Ы. "практичность" этой части тоже "так себе", уж очень там примеры "непрактичные" :)

    ОтветитьУдалить
  3. По поводу непрактичных примеров - скорее соглашусь. Но что важно - одни и те же. Т.е. можно сравнить, как настраивать контейнеры под разными frameworks. Документация с контейнером описывает 1 контейнер, но не сравнивает его с другими. Здесь же была цель (как я вижу). Взять одну задачу и показать ее в разных контейнерах. В этом ее ценность.

    ОтветитьУдалить
  4. Идея сравнения контейнеров показалась интересной, меня не вдохновила реализация:)

    ОтветитьУдалить
  5. 4 часть - это просто справочник по контейнерам. 4 часть - тоже полезна.

    ОтветитьУдалить
    Ответы
    1. Виктор, полезность - штука субъективная. По сравнению с другими частями, в которых описываются ключевые особенности управления зависимостями, справочник мне показался излишним. Так сказать эта часть уменьшила cohesion книги и нарушила Single Responcibility Principle:)

      Удалить
  6. Прочитал, кстати, ваш отзыв Сергей и решил приобрести. Как по мне - книга на троечку. Не слишком глубока для тех, кто уже имел дело с DI и имеет представление об IoC. Этих людей при прочтении ждет разочарование в части описания мотивации. Сложно привести пример, но практически во всех освещенных вопросах. Части книги с точками входа в различных типах приложений и описанием IoC-контейнеров вообще можно было опустить. Как только имеешь дело хоть с одним IoC, остальное читаешь в его документации. А если вернуться к примерам - ну, вот хотя бы схема 4.12 на стр. 178. Там ее без собственного опыта не осмыслишь, если просто надеется на его описание. Оно очень скудное, а потому алгоритм бесполезен. Видно, что у человека есть опыт, но при этом донести мотивацию в вопросе выбора или применения техник явно недостаточно. Многое приходится додумывать.

    ОтветитьУдалить
    Ответы
    1. Олег, мне кажется степень восприятия книги очень сильно зависит от уровня читателя (и у тебя (думаю, разумно на "ты") в комментарии об этом тоже упоминается). Да, эта книга будет бесполезной новичку в дизайне (именно в дизайне, а не в DI), и точно будет бесполезна эксперту в DI.
      У меня во время чтения этой книги был большой опыт в дизайне и маленький в классическом ныне DI. Именно поэтому мне многие советы показались полезными, да и изложение дает достаточно основательное представление об этой теме, вводит обильное число паттернов.

      Книги далека от идеала, но ведь, согласись, ничего лучшего на рынке и нет, книга в своем роде уникальна.

      Удалить
    2. Согласен, Сергей, уникальна. Хотя, я в свое время гонялся за "Applied ASP.NET MVC 3 in Context". Надеялся, что там будет нечто подобное, хоть пара глав. Вот теперь думаю - стоит ли прочитать http://www.growing-object-oriented-software.com/ :)

      Удалить
  7. А, ну или где-то он там писал про 14-страничный доклад в своей компании на проекте, где он журил народ за то, что тот не соблюдает принципы DI. При этом в раздел 1.2.2 "достоинства внедрения зависимостей" занимает 3 листа, после прочтения которых ты не можешь до конца объяснить JD в чем все-таки преимущество соблюдения принципа. Ну, кроме возможности модульного тестирования, хорошо. Это у него очевидно и даже с примерчиком. А для остального - не постарался.

    ОтветитьУдалить
  8. wtf? Купил книгу - оказалось 460 страниц, вместо 624 страницы в электронном варианте. Просто выкинули 200 страниц, Castle Windsor и StructureMap есть - всего остального нет. Даже в описании книги написано, что есть описание Unity. Но его нет.
    Обидно очень, несмотря на маленький КПД, как вы говорите.

    ОтветитьУдалить
    Ответы
    1. Так это же ревью оригинальной книги, а вы, видимо, купили перевод.

      Удалить
  9. >Добавление Швов в приложение требует дополнительных усилий, поэтому добавлять их стоит только в случае необходимости.

    В части 6.1.2. "Пример: выбор алгоритма расчета пути" (в русском переводе) что-то странное. Обычная фабрика превращается в "абстрактную" путём замены конкретной реализации фабрики её двойником-интерфейсом. При этом представить осмысленную альтернативную реализацию интерфейса IRouteAlgorithmFactory мне кажется задачей довольно затруднительной: ведь смысл фабрики заключается как раз в том, чтобы быть единственной точкой в приложении, где происходит нужный резолвинг. Мне кажется, что здесь Марк пошёл как раз против своих собственных слов и руководствовался не столько реальными потребностями дизайна, сколько евангелическими принципами: никакой статики, никаких new(), никаких конкретных типов.

    ОтветитьУдалить