Третья редакция не выходила на русском. А большинство разработчиков не читает книги на английском при наличии русского перевода. Да и русский перевод многие читают наискосок — посмотрите на любых форумах на обсуждения по Dispose/Finalize — сплошные мифы и домыслы. А ведь он тоже в Рихтере расписан, в той же главе.
У Рихтера есть не всё, а только очень небольшая выжимка. Особенно если это 2-е издание на русском языке.
GCLatencyMode не упомянут вообще. Особенности работы с Pinned-объектами — тоже, насколько я знаю. Особенности для многоядерных систем — только вскользь, одним предложением. Для спокойной разработки Рихтера хватит. Для устранения проблем с памятью — скорее всего нет.
LINQ нельзя реализовать на 100% поверх SQL, т.к. такая реализация должна уметь отобразить в SQL вообще любой метод. Абстракция дырява — но Query из статьи — это еще один уровень абстракции поверх нее. В статье, например, никак не показано то самое повторное использование кода, ради которого все затевалось. Вообще все, что делает код в статье — это фильтрация уже готового IQueryable по тривиальному условию. Вы пробовали написать по аналогии что-то чуть более сложное?
Попробуйте написать пример с повторным использованием, у которого в случае IQueryable будут проблемы с дырявой абстракцией, а в случае с Query (поверх ILinqProvider, поверх того же IQueryable) — нет.
Не вытаскивайте IQueryable за определенные границы (например, вы выдавайте его за пределы BLL). И не получите проблем с соединением. В абстрактной системе в вакууме IQueryable применим в тех же границах, что и IQueryFor/IQueryFactory. Не знаю, какой магией IQueryFor можно заставить не жить слишком долго уже после доставания из контейнера, но эта же магия наверняка применима к IQueryable.
Ок, если коротко — для вложения столь значительных затрат в реализацию, и столь значительного увеличения сложности должна быть соизмеримая Проблема. Ок, она есть — «типа-репозитории» с тысячами методов. Если есть альтернативное решение — IQueryable Repository + Extension Methods — с очевидно меньшей сложностью, требующее в разы меньше кода, позволяющее использовать композицию, а не наследование, поддерживаемое базовым фреймворком, с гораздо более низким порогом вхождения — то новое решение должно предлагать Огромное Преимущество.
Если преимущества нет, или оно уровня «конфликтов станет меньше на 2%» — то новое решение — это не архитектурное решение, а добавление фабрики в ваш алгоритм.
Если учесть, что студия хранит список файлов в файле проекта, то эти два программиста будут ловить конфликты в любом случае. Даже чаще, чем в случае с god-репозиторием — в репозитории они смогут дописывать методы в заведомо разные места. А студия при добавлении новых файлов будет править cproj всегда в одном и том же месте.
А чем вас не устроил стандартный для C# механизм IQueryable (Query), Expression (ICriterion), IQueryProvider (IQueryFactory)?
Пробовали ли вообще использовать IQueryable Pipes and Filters в связке с IQueryable Repository — ведь они дают то же, что ваш подход — буквально парой строк кода. Если да, то по какой причине от них отказались в пользу объемной самописной обертки?
Причина была достаточно веской — например, полное отсутствие юнит-тестов. Т.к. принятое решение заменить фильтры на «разрешено только те выборки, для которых написано не менее 2-х классов и 40-ка строк кода» явно создает оверхед при разработке. И, при наличии тестов, ничем не безопаснее и надежнее обычного однострочного public static IQueryable WithLogin(this IQueryable, string login) {… }.
Repository не обязательно предполагает добавления объектов в коллекцию. Просто все используют его как точку к доступа к данным, а не заморачиваются с SRP до уровня «каждому if-у — по классу».
Спецификация у того же Фаулера тоже разбита на критерий и какую-то абстрактную «in memory strategy» выборки. И у репозитория есть всего один метод matching(aCriteria). Так что разница IQueryFactory и IRepository — только в названии.
В статье — переименованная самописная реализация паттерна Repository. Естественно, работающая за счет существования в C# встроенной реализации паттерна Repository под названием IQueryProvider.
Компоненты студии — это зависимости уровня сборок/групп сборок. Загрузка сборки — долгая и дорогая по памяти операция. И она сама по себе Lazy в .NET — тянется и компилируется только необходимый минимум. Но даже при этом студия стартует целых 4 секунды на моей машине.
Статья решает проблему зависимостей уровня классов. Создание десятка объектов — дешевая по памяти и времени операция, так что ничего общего с загрузкой студии она не имеет.
Я же не говорю, что идея плохая. Просто спросил про конкретное практическое примерение, а не абстрактные 10 подсистем. Репозиторий из статьи занимает 12 байт, инициализируется мгновенно. Сколько на практике должно быть классов в графе зависимостей, чтобы хоть немного выйти в плюс с подходом их статьи? По сравнению, например, с резолвом сразу всего графа через тот же Unity?
Т.е. я могу представить, например, большое «правильное» ООП приложение, с логикой в BE, а не классах-сервисах. Но в нем вариант «все lazy, и нет управления временем жизни» только ухудшит ситуацию.
Дерево/файл/документ — это Lazy Load данных, вполне применимый паттерн. Загрузка каждого куска данных — сравнительно дорогая операция. Создание объекта в .net — наоборот, очень дешевая.
Статья не про Lazy-паттерны вообще. Она про Lazy Dependency Resolution. Про решение проблемы тормозов при разрешении зависимостей.
Может быть, я как-то неправильно читаю статью, но суть ее для меня примерно такая:
«предположим, тормозит кривой код в конструкторе подсистемы сериализации — сделаем по всему проекту lazy di, и тормозов почти никто не заметит». Если статья предлагает решение — то она должна описывать возможные негативные последствия, и указывать альтернативы. Иначе следующий разработчик будет проклинать и PostSharp, и все Lazy-паттерны, и автора заодно с переводчиком.
Вы сами использовали пример из статьи на практике? Получили хоть какой-то ощутимый плюс?
MEF предназначен для написания аддонов, а не для чистого IoC/DI. Тот же Unity понимает Lazy: [Dependency]
private Lazy<IProductRepository> ProductRepository {get; set;}
Меня идея загружать лениво вообще все зависимости в проекте немного смущает. Lazy Load не повышает производительность. Скорее наоборот, ухудшает и прячет проблемы. Нужно исправлять причину, а не следствие. Если есть проблема с инициализацией конкретного класса — то нужно делать явную ленивую инициализацию внутри него, выносить инициализацию в отдельный поток, а не вставлять поспроцессором тысячи проверок по всему коду. Иначе со временем получите эффект «все равномерно тормозит».
Оценка сложности делается не в часах, или в крайнем случае — в «идеальных человеко-часах». Немного странно убеждать заказчика в том, что сложность проекта 632, а не 423 попугаев.
Как менеджер, могу сказать что сложность для меня не синоним времени. Для не-аутсорсовых проектов отслеживание сложности/скорости это в первую очередь способ узнать войдет ли фича X в следующий релиз, или способ назвать более-менее правдоподобную дату релиза и осмысленно выбросить из него фичи. А если мененджер просто пересчитывает 1 sp = 1 человекодень, не учитывая скорость, и "«планирует» создание системы для которой нужно пол-года на месяц" то он сам роет себе яму.
Сложность — это не обязательно человеко/часы. Точнее, чаще всего это именно не человеко-часы. Равномерное завышение сложности командой никак не влияет на окончательные сроки, и не создает свободное время для разработчиков. Оно просто пропорционально поднимает velocity. Хитрый метод позволяет выявить неравномерность в оценках. Найденную неравномерность, теоретически, стоит проанализировать и сделать из нее выводы — почему оценка для конкретной задачи была завышена или занижена. Практически — скорее всего ценность выводов не окупит расходов на анализ.
Окончательный результат, на мой взгляд, не слишком отличается от быстрого T-Shirt Sizing — M/L/S/S для четырех задач из примера. Точно вычисленную теоретическую разницу между 4.52 и 5.08 легко разрушает лишняя чашка кофе/интересный пост на хабре/неправильно выбранная нога для вставания утром у одного из разработчиков.
вообще говоря транзакции — дело клиентского по отношению к DAL/репозиторию/следующему-баззвордному-паттерну кода.
Я тоже не понял вопроса. И вашего же ответа.
DAL должен поддерживать транзакции, а не организовывать их. Репозиторий [в дописанном до совместимости с TransactionScope варианте, гарантированно не делающий эскалацию внутри] будет поддерживать и Explicit и Implicit транзакции. Если автор решил не реализовать поддержку транзакций в рамках статью — это его выбор. Это же статья, а не дамп кода живого проекта с комментариями.
GCLatencyMode не упомянут вообще. Особенности работы с Pinned-объектами — тоже, насколько я знаю. Особенности для многоядерных систем — только вскользь, одним предложением. Для спокойной разработки Рихтера хватит. Для устранения проблем с памятью — скорее всего нет.
Попробуйте написать пример с повторным использованием, у которого в случае IQueryable будут проблемы с дырявой абстракцией, а в случае с Query (поверх ILinqProvider, поверх того же IQueryable) — нет.
Не вытаскивайте IQueryable за определенные границы (например, вы выдавайте его за пределы BLL). И не получите проблем с соединением. В абстрактной системе в вакууме IQueryable применим в тех же границах, что и IQueryFor/IQueryFactory. Не знаю, какой магией IQueryFor можно заставить не жить слишком долго уже после доставания из контейнера, но эта же магия наверняка применима к IQueryable.
Ок, если коротко — для вложения столь значительных затрат в реализацию, и столь значительного увеличения сложности должна быть соизмеримая Проблема. Ок, она есть — «типа-репозитории» с тысячами методов. Если есть альтернативное решение — IQueryable Repository + Extension Methods — с очевидно меньшей сложностью, требующее в разы меньше кода, позволяющее использовать композицию, а не наследование, поддерживаемое базовым фреймворком, с гораздо более низким порогом вхождения — то новое решение должно предлагать Огромное Преимущество.
Если преимущества нет, или оно уровня «конфликтов станет меньше на 2%» — то новое решение — это не архитектурное решение, а добавление фабрики в ваш алгоритм.
Пробовали ли вообще использовать IQueryable Pipes and Filters в связке с IQueryable Repository — ведь они дают то же, что ваш подход — буквально парой строк кода. Если да, то по какой причине от них отказались в пользу объемной самописной обертки?
Причина была достаточно веской — например, полное отсутствие юнит-тестов. Т.к. принятое решение заменить фильтры на «разрешено только те выборки, для которых написано не менее 2-х классов и 40-ка строк кода» явно создает оверхед при разработке. И, при наличии тестов, ничем не безопаснее и надежнее обычного однострочного public static IQueryable WithLogin(this IQueryable, string login) {… }.
Спецификация у того же Фаулера тоже разбита на критерий и какую-то абстрактную «in memory strategy» выборки. И у репозитория есть всего один метод matching(aCriteria). Так что разница IQueryFactory и IRepository — только в названии.
В статье — переименованная самописная реализация паттерна Repository. Естественно, работающая за счет существования в C# встроенной реализации паттерна Repository под названием IQueryProvider.
Статья решает проблему зависимостей уровня классов. Создание десятка объектов — дешевая по памяти и времени операция, так что ничего общего с загрузкой студии она не имеет.
Т.е. я могу представить, например, большое «правильное» ООП приложение, с логикой в BE, а не классах-сервисах. Но в нем вариант «все lazy, и нет управления временем жизни» только ухудшит ситуацию.
Статья не про Lazy-паттерны вообще. Она про Lazy Dependency Resolution. Про решение проблемы тормозов при разрешении зависимостей.
Может быть, я как-то неправильно читаю статью, но суть ее для меня примерно такая:
«предположим, тормозит кривой код в конструкторе подсистемы сериализации — сделаем по всему проекту lazy di, и тормозов почти никто не заметит». Если статья предлагает решение — то она должна описывать возможные негативные последствия, и указывать альтернативы. Иначе следующий разработчик будет проклинать и PostSharp, и все Lazy-паттерны, и автора заодно с переводчиком.
Вы сами использовали пример из статьи на практике? Получили хоть какой-то ощутимый плюс?
[Dependency]
private Lazy<IProductRepository> ProductRepository {get; set;}
Меня идея загружать лениво вообще все зависимости в проекте немного смущает. Lazy Load не повышает производительность. Скорее наоборот, ухудшает и прячет проблемы. Нужно исправлять причину, а не следствие. Если есть проблема с инициализацией конкретного класса — то нужно делать явную ленивую инициализацию внутри него, выносить инициализацию в отдельный поток, а не вставлять поспроцессором тысячи проверок по всему коду. Иначе со временем получите эффект «все равномерно тормозит».
Я тоже не понял вопроса. И вашего же ответа.
DAL должен поддерживать транзакции, а не организовывать их. Репозиторий [в дописанном до совместимости с TransactionScope варианте, гарантированно не делающий эскалацию внутри] будет поддерживать и Explicit и Implicit транзакции. Если автор решил не реализовать поддержку транзакций в рамках статью — это его выбор. Это же статья, а не дамп кода живого проекта с комментариями.