Как стать автором
Обновить

Комментарии 13

ObjectFactory.GetInstance()
В принципе тоже не совсем серебряная пуля. по хорошему контейнер должен передаваться в контексте, иначе разные компоненты не смогут получать разную реализацию интерфейса.
Это же пример :) Тут также не хватает double-checked lock'a для многопоточных вариантов, и прочих рюшечек, которые необходимы в конкретных проектах
Имхо, если ленивая загрузка сделана в форме if (x == null) x = new X(); return x;, можно смело использовать Lazy<T>
а если вам в дальнейшем понадобится расширять / менять? Неудобно же будет для каждого такого свойства переписывать код
Тут вопрос стоял про ленивую загрузку в контейнере, вроде бы. Контейнеры уже знают про Lazy<T>. Если нужно другое поведение, оно прописывается в контейнер как фабричный метод (Func<T>). Так что код нужно переписать только в одном месте.
Можно использовать и Lazy. Суть примера же не в том, что вот есть такой код, и по-другому никто не пишет. Суть примера в том, как можно избавиться от шаблонного кода. В том же MEF делается аналогично ([Import], [Export], например), только там насколько я помню, рефлексия. Здесь можно сделать проверки на этапе компиляции, и на этот же этап вынести кодогенерацию, что ускорит работу приложения. Мне, например, в MEF не нравилось что зависимости довольно долго грузятся. Конечно, не так что, минуту жду, время приемлимое… Но для современного уровня технологий, долговато. Если делать постобработку постшарпом, то можно добиться более высоких показателей производительности при решении задачи, чем если бы этим занимался real-time фреймворк.
MEF предназначен для написания аддонов, а не для чистого IoC/DI. Тот же Unity понимает Lazy:
[Dependency]
private Lazy<IProductRepository> ProductRepository {get; set;}

Меня идея загружать лениво вообще все зависимости в проекте немного смущает. Lazy Load не повышает производительность. Скорее наоборот, ухудшает и прячет проблемы. Нужно исправлять причину, а не следствие. Если есть проблема с инициализацией конкретного класса — то нужно делать явную ленивую инициализацию внутри него, выносить инициализацию в отдельный поток, а не вставлять поспроцессором тысячи проверок по всему коду. Иначе со временем получите эффект «все равномерно тормозит».
Ну, например, я видел отличный пример использования Lazy — mono::cecil. Вместо того чтобы парсить весь файл, и загружать в память всю программу, та загружается по мере необходимости. Соответственно, меньше ресурсов тратится если нужны некоторые части загруженного документа, а не весь документ, и это хорошо. При выборе lazy-loading необходимо как и везде, понимать, что ускорит а что замедлит. Понятно что если в данном месте ленивая инициализация не нужна, ее и не надо использовать :) Например, если понятно что грузиться будет каждый элемент какого-то дерева, причем сразу же будут необходимы все узлы дерева.

Задача статьи — показать пример. Задача разработчика — применить к своим задачам и понять, будет ли программа по скорости работы и разработке в плюсе при выборе.
Дерево/файл/документ — это Lazy Load данных, вполне применимый паттерн. Загрузка каждого куска данных — сравнительно дорогая операция. Создание объекта в .net — наоборот, очень дешевая.

Статья не про Lazy-паттерны вообще. Она про Lazy Dependency Resolution. Про решение проблемы тормозов при разрешении зависимостей.
Может быть, я как-то неправильно читаю статью, но суть ее для меня примерно такая:
«предположим, тормозит кривой код в конструкторе подсистемы сериализации — сделаем по всему проекту lazy di, и тормозов почти никто не заметит». Если статья предлагает решение — то она должна описывать возможные негативные последствия, и указывать альтернативы. Иначе следующий разработчик будет проклинать и PostSharp, и все Lazy-паттерны, и автора заодно с переводчиком.

Вы сами использовали пример из статьи на практике? Получили хоть какой-то ощутимый плюс?
Не совсем так… пусть есть некоторое количество подсистем, которые из-за своей специфичности в 90% времени не используются, но их загрузка приводит к просадке по памяти и по производительности. Так пусть они загружаются только тогда когда они нам понадобятся. А если этих систем не одна, а 10, например… Будет не удобно делать точки доступа к ним, поскольку они все будут одинаковыми. Тогда код, отвечающий за точку доступа выносится в аспект, и по сути находится в одном месте, автоматически «размножаясь» при компиляции ))
Я же не говорю, что идея плохая. Просто спросил про конкретное практическое примерение, а не абстрактные 10 подсистем. Репозиторий из статьи занимает 12 байт, инициализируется мгновенно. Сколько на практике должно быть классов в графе зависимостей, чтобы хоть немного выйти в плюс с подходом их статьи? По сравнению, например, с резолвом сразу всего графа через тот же Unity?

Т.е. я могу представить, например, большое «правильное» ООП приложение, с логикой в BE, а не классах-сервисах. Но в нем вариант «все lazy, и нет управления временем жизни» только ухудшит ситуацию.
Ну мне кажется, что если бы та же Visual Studio грузила бы свои компоненты только по необходимости, ее старт был бы моментален (я вот не использую доступ к базе данных из студии, не пользуюсь TFS, справкой, и т.д.). Тут, конечно, стоит добавить некую статистику использования компонент и загружать наиболее используемые на старте, чтобы не проседать во время работы, однако редко запускаемые вещи пусть загружаются только когда они необходимы. Однако тянуть все что есть не стоит…
Компоненты студии — это зависимости уровня сборок/групп сборок. Загрузка сборки — долгая и дорогая по памяти операция. И она сама по себе Lazy в .NET — тянется и компилируется только необходимый минимум. Но даже при этом студия стартует целых 4 секунды на моей машине.
Статья решает проблему зависимостей уровня классов. Создание десятка объектов — дешевая по памяти и времени операция, так что ничего общего с загрузкой студии она не имеет.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории