Re[11]: роль ООП при работе с данными
От: Ziaw Россия  
Дата: 28.10.08 12:31
Оценка: 8 (1) +1
Здравствуйте, IB, Вы писали:

IB>Формально, таки да то что ты называешь "теркингом" может быть как в стройной модели, так и может быть жирная модель без "трекинга". Но на практике, и в многочисленных примерах, я не встречал жирной модели без "трекинга", а про стройную с "трекингом" только упоминания слышал.


Я рожал обе Rich модель без трекинга хорошо себя показала в десктоп приложении, anemic с ограниченным применением трекинга пока неплох в трехзвенке.

Говорю пока, т.к. до внедрения она еще не дошла. Но за полтора года проектирования и разработки серьезных нареканий не вызвала.
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[11]: роль ООП при работе с данными
От: Torvin  
Дата: 28.10.08 19:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Torvin, Вы писали:

T>>для строк immutable — это эмуляция поведения value-типа. для сложных объектов (т.е. состоящих из нескольких полей) я себе этого не представляю
S>Для улучшения представлений рекомендую познакомиться с DateTime.
S>Ты в курсе того, что в Яве приходится со страшной силой приседать, чтобы только не дать стороннему коду надругаться над person.GetBirthday() произвольным образом? Это только потому, что даты в яве — mutable.
S>Практика применения показывает, что никаких трудностей со сложными датами у программистов не возникает.
давайте подумаем внимательно вместе. во-первых дата — это не несколько полей а одно — количество тиков прошедших с некоторой даты X. но это не важно. важно то, что "во-вторых"
во-вторых, посмотрим почему для даты уместо immutable-поведение? скажем, поменяем дате год. теперь у нас два объекта, скажем 18.10.2008 и 18.10.1937
это разные даты? да! точно такая же логика и со строками
а теперь поменяем имя проекту. если проект immutable, то у нас тоже будет 2 разные сущности. но действительно ли это разные объекты? нет. это тот же самый проект, просто пользователь исправил опечатку которую допустил при его создании. чувствуете разницу?

разве я не прав?..

PS
я не знаю как там в джаве, никогда не писал, но уверен что все решается в духе
return _birthday.Clone();
или там
return new DateTime(_birthday.GetTicks());
это конечно не есть гуд, но мы сейчас не о проблемах в джаве
Re[11]: роль ООП при работе с данными
От: Torvin  
Дата: 28.10.08 19:30
Оценка:
Здравствуйте, IB, Вы писали:

T>>для сложных объектов (т.е. состоящих из нескольких полей) я себе этого не представляю

IB>Ну и напрасно, имеет смысл представить. От количества полей тут ничего не зависит.
если вас не затруднит — прочтите пожалуйста соседнее мое сообщение по этому поводу Sinclair'у

T>>ну мы ведь не на лиспе пишем сферического коня в вакууме. в том и проблема

IB>На лиспе, к слову GC в .Net написан, так что не надо про сферических коней.. )
на сколько я помню, на лиспе писался только первый вариант GC. и, как сказал автор, "потому что у лиспа такой прикольный синтаксис!" (к сожалению не могу найти ссылку на источник с цитатой)
кроме того, GC это просто реализация некоего алгоритма. логика известна, структуры данных известны, и ничего никогда не поменяется. хоть на голом Си пиши и вылизывай до бесконечности профайлером. и практически плевать на maintainability
сферический конь и есть


T>> просто в нашем случае по крайней мере известно где искать грабли

IB>Я предпочитаю, чтобы за меня их находил компилятор.
T>>любому нормальному человеку, пишущему код на основе жирной модели никогда не придет в голову делать поле ID writable. потому что в этом нет никакой необходимости
IB>Ты сам себе противоречишь. Почему в одном случае ID обязательно делать изменяемым, чтобы избежать каких-то мифических проблем с immutable, а в другом этого делать нет никакой необходимости?

похоже мы друг-друга не понимаем. приведите пожалуйста на всякий случай свое определение термина immutable. я лично считаю что объект immutable в том случае, если после создания его поля никогда не меняются. ни "изнутри" объекта, ни снаружи какими-то "менеджерами". а Вы?
Re[12]: роль ООП при работе с данными
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 28.10.08 19:45
Оценка:
Здравствуйте, Torvin, Вы писали:

T>во-вторых, посмотрим почему для даты уместо immutable-поведение? скажем, поменяем дате год. теперь у нас два объекта, скажем 18.10.2008 и 18.10.1937

T>это разные даты? да! точно такая же логика и со строками
T>а теперь поменяем имя проекту. если проект immutable, то у нас тоже будет 2 разные сущности. но действительно ли это разные объекты? нет. это тот же самый проект, просто пользователь исправил опечатку которую допустил при его создании. чувствуете разницу?
Неубедительно. Почему это один объект с точки зрения программы?
Re[13]: роль ООП при работе с данными
От: Torvin  
Дата: 28.10.08 19:53
Оценка:
Здравствуйте, gandjustas, Вы писали:

T>>во-вторых, посмотрим почему для даты уместо immutable-поведение? скажем, поменяем дате год. теперь у нас два объекта, скажем 18.10.2008 и 18.10.1937

T>>это разные даты? да! точно такая же логика и со строками
T>>а теперь поменяем имя проекту. если проект immutable, то у нас тоже будет 2 разные сущности. но действительно ли это разные объекты? нет. это тот же самый проект, просто пользователь исправил опечатку которую допустил при его создании. чувствуете разницу?
G>Неубедительно. Почему это один объект с точки зрения программы?
с точки зрения программы он может быть и одним и не одним. как напишем так и будет. я пытаюсь заставить задаться вопросом почему стоит делать так а не иначе. вопрос целесообразности, понимаете?
почему по-вашему DateTime и String являются immutable в .NET (кстати один из них ref- а другой value-type, и это не прихоть разработчиков, на это есть причина, но мы сейчас не об этом). почему, например, какой-нибудь захудалый System.Windows.Forms.Timer не immutable?.....
Re[12]: роль ООП при работе с данными
От: IB Австрия http://rsdn.ru
Дата: 28.10.08 22:07
Оценка:
Здравствуйте, Torvin, Вы писали:

T>на сколько я помню, на лиспе писался только первый вариант GC.

Совершенно верно, но сути дела это не меняет.

T>сферический конь и есть

Я правильно понимаю, что ты считаешь, что функциональные языки настолько акодемичны, что на них нельзя решить реальные прикладные задачи?

T> я лично считаю что объект immutable в том случае, если после создания его поля никогда не меняются.

Как это определение влияет на то, что в одном случае ID можно сделать readonly, а в другом почему-то нельзя?
Мы уже победили, просто это еще не так заметно...
Re[12]: роль ООП при работе с данными
От: IB Австрия http://rsdn.ru
Дата: 28.10.08 22:40
Оценка:
Здравствуйте, Torvin, Вы писали:

T> но действительно ли это разные объекты? нет.

Да, это два разных проекта. Точнее так, с точки зрения пользователя это зависит от сценария.
С точки же зрения архитектуры приложения, правильнее считать, что это два разных объекта. Такое допущение позволяет намного проще разруливать любые коллизии с проектами.

T> это тот же самый проект, просто пользователь исправил опечатку которую допустил при его создании. чувствуете разницу?

Понимаешь, есть такой нюанс. Скажем, для финансовой отчетности важно знать, что когда этот проект использовался до исправления, то он назывался так, а когда после, то по другому. Чувствуешь разницу?
В сознании пользователя это может быть и один объект, а с точки зрения приложения очень удобно иметь доступ ко всем его предыдущим состояниям, как к отдельным самостоятельным объектам.

T>разве я не прав?..

Нет.. )
Мы уже победили, просто это еще не так заметно...
Re[14]: роль ООП при работе с данными
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 29.10.08 05:18
Оценка:
Здравствуйте, Torvin, Вы писали:

T>Здравствуйте, gandjustas, Вы писали:


T>>>во-вторых, посмотрим почему для даты уместо immutable-поведение? скажем, поменяем дате год. теперь у нас два объекта, скажем 18.10.2008 и 18.10.1937

T>>>это разные даты? да! точно такая же логика и со строками
T>>>а теперь поменяем имя проекту. если проект immutable, то у нас тоже будет 2 разные сущности. но действительно ли это разные объекты? нет. это тот же самый проект, просто пользователь исправил опечатку которую допустил при его создании. чувствуете разницу?
G>>Неубедительно. Почему это один объект с точки зрения программы?
T>с точки зрения программы он может быть и одним и не одним. как напишем так и будет. я пытаюсь заставить задаться вопросом почему стоит делать так а не иначе. вопрос целесообразности, понимаете?
Понимаю. Также понимаю что в случае разных объектов после изменения одного поля гораздо проще обеспечить optimistic concurrency.

T>почему по-вашему DateTime и String являются immutable в .NET (кстати один из них ref- а другой value-type, и это не прихоть разработчиков, на это есть причина, но мы сейчас не об этом). почему, например, какой-нибудь захудалый System.Windows.Forms.Timer не immutable?.....

Timer — внешний ресурс, как файл или соединение с БД. Хотя и его можно было сделать immutable но такие решения только усложняют использование.
Re: роль ООП при работе с данными
От: Аноним  
Дата: 29.10.08 13:43
Оценка: +1 -1
Здравствуйте, QrystaL, Вы писали:

QL>Заинтересовала статья: http://blogs.gotdotnet.ru/personal/bezzus/PermaLink.aspx?guid=7a6a69bd-bedf-425f-b09c-123b4a41f686


QL>Кто что скажет?


Здравствуйте, QrystaL, Вы писали:

QL>Заинтересовала статья: http://blogs.gotdotnet.ru/personal/bezzus/PermaLink.aspx?guid=7a6a69bd-bedf-425f-b09c-123b4a41f686

QL>Кто что скажет?

По-моему, происходит массовое непонимание/недопонимание друг друга. Одни (в том числе автор оригинальной статьи) воспринимает анемичную модель как "вааще анемичную" — POD-объекты (максимум — аксессоры к ним), и не более. Его противники считают, что объекты рассматриваемой предметной области — это несколько не то, что лежит в БД; код, который обеспечивает соответствие (вернее, восстанавливает потерянное identity при преобразованиях OOP<->Relation) обязан быть, и его присутствие в модели предметной области не определяет однозначно, что это — уже жирная модель.

Поясню. Сторонники анемичной модели настаивают на следующей схеме:
DB <-> Data Objects (anemic model) <-> Rich OO Model + Services.

В то время, как "жирная" модель хочет обойтись такой схемой:
DB <-> Rich Domain Model + Services (optionally).

При этом в первом случае вы руками пишете код, который обеспечивает соответвие identity, равномерно распределяя его по бизнес-логике (пример — ручной lazy loading и коллекции), во втором — к этому привлекаются механизмы OR/M (как, например, в NHibernate).

При этом как-то незаметно подменяется спор о Anemic .vs. Rich спором о том, кто правильнее реализует Domain Model в частном случае применения в OR/M, и из этого делаются неаргументированные выводы в пользу EF. (И причем здесь ООП?). Аналогично подменяется понятие OOP Data на Relation Data, которые, мягко говоря, далеки от ООП.

И мой взмах мечом в холиваре — EF не способствует написанию в анемичном стиле, он просто не способен полноценно поддерживать "жирную" модель.
Спасибо.
Re[13]: роль ООП при работе с данными
От: Torvin  
Дата: 03.11.08 17:53
Оценка:
IB>Понимаешь, есть такой нюанс. Скажем, для финансовой отчетности важно знать, что когда этот проект использовался до исправления, то он назывался так, а когда после, то по другому. Чувствуешь разницу?
IB>В сознании пользователя это может быть и один объект, а с точки зрения приложения очень удобно иметь доступ ко всем его предыдущим состояниям, как к отдельным самостоятельным объектам.
ну это очччень частный случай. даже если он встречается Вам чаще чем другие случаи — это еще не дает повод клепать все подряд объекты как 'immutable' — "авось пригодится"

G>Понимаю. Также понимаю что в случае разных объектов после изменения одного поля гораздо проще обеспечить optimistic concurrency.

действительно. поэтому в разных дата-сессиях объекты и должны быть разными. но в одной датасессии?!... какая-такая там concurrency?
Re[13]: роль ООП при работе с данными
От: Torvin  
Дата: 03.11.08 18:00
Оценка:
вдогонку

T>сферический конь и есть

IB>Я правильно понимаю, что ты считаешь, что функциональные языки настолько акодемичны, что на них нельзя решить реальные прикладные задачи?
одно из другого не следует, и я не особо силен в области функциональных языков.
просто пример с реализацией GC на Lisp "потому что у него прикольный синтаксис" (с) явно является плохим аргументом в нашей дискуссии
Re[9]: роль ООП при работе с данными
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 14.11.08 11:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Это, конечно, не единственный способ построить MVP архитектуру, но в принципе вполне жизнеспособный. Поясняю: в этом подходе данные достаются из SQL сервера за один (1) прием. Есть гарантия того, что никакая ленивая зараза не потащит какой-то хвост из базы в момент, когда уже Render поехал. Далее, нет никакого кэша объектов, который живет своей жизнью, жрет память и требует синхронизации. Всё, мы датасет забрали, теперь можно отпустить SQL сервер заниматься другой полезной работой. Импульсный режим для серверов — это самый сенокос.

У меня именно по варианту начальника (Re[7]: роль ООП при работе с данными
Автор: Mmmaloy
Дата: 21.10.08
) работает система, в которой вполне себе анемичные модели, но при этом вместо Datasets задействован NHibernate, частично ленивая загрузка (проксирование) и несколько кешей (Identity Map & Query Cache). NHibernate позволяет построить как anemic, так rich модели. Не вижу озвученного автором антогонизма NHibernate и Anemic model. Если же Entity Framework умеет только первый подход (anemic), то высказывание о недостатках NHibernate вначале статьи и о достоинствах EF в конце статьи, мне кажется, автору стоило озвучить с другим акцентом.

S>Теперь у нас, значит, поехала трансформация. В чем начальник прав? С тем, что редкий databound контрол в aspx сравнится по скорострельности отплевывания форматированного аутпута со старым добрым откомпилированным XSLT. Я надеюсь, все в курсе, что текстовый XSLT сначала парсится, потом по нему генерируется MSIL код, который затем обрабатывается джитом, и в итоге мы имеем самый что ни на есть нативный код, который шарашит по твоему XML как казак по степи?

Насколько знаю, старый добрый XSLT в .NET — XslTransform — вовсе не компилированный, а компилируется в MSIL и соответственно оптимизируется JIT-ом новый — XslCompiledTransform.
Re: роль ООП при работе с данными
От: Воронков Василий Россия  
Дата: 22.11.08 22:00
Оценка:
Здравствуйте, QrystaL, Вы писали:

QL>Заинтересовала статья: http://blogs.gotdotnet.ru/personal/bezzus/PermaLink.aspx?guid=7a6a69bd-bedf-425f-b09c-123b4a41f686

QL>Кто что скажет?

А мне не понравилась статья. Это называется взять одно архитектурное решение и довести его до абсурда. После прочтения статьи вообще складывается ощущение, будто бы тот же lazy loading в жирной модели вообще прикручивают только для того, чтобы не оставлять пустыми всякие OrderItems, когда они не заполняются т.к. не нужны. Так если они не нужны зачем вообще lazy loading? Почему бы не сделать так:

class CustomerFrame
{
  int Id { get; }
  string Name { get; set; }
}

class Customer : CustomerFrame
{
  OrderItemList Orders { get; }
}


Да и вообще, честно, вся эта проблема жирной версус тонкой модели кажется мне немного надуманной. У ООП есть одна фишка, к-я там вскользь упомянута, называется инкапсуляция. Инкапсуляция означает, что клиентскому коду должно быть насрать, что там конкретно происходит, когда вызывается какой-нибудь Order.Save.
Не исключено, конечно, что кто-нибудь — как это живописно описывается в статье — там прям внутри одного метода и логику БД описывает и сериализацию в ХМЛ да еще и сетевую передачу вдобавок. Но тут наверное не жирная модель виновата, это уже с мозгом что-то.
Кто мешает сделать так:

class Order<T> where T : IPersistanceModel
{
  void Save()
  {
    persistanceModel.Save(this);
  }
}

Order<Database> order = ...
order.Save();


Или даже не выносить эту самую persistance model в интерфейс, если эти не нужно. Клиентский код при это вообще не будет знать, жирно там или не жирно. А все фишки жирной модели и "более объектно-ориентированного подхода" — налицо.
Re[2]: роль ООП при работе с данными
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.11.08 07:02
Оценка: 19 (5) +2
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Да и вообще, честно, вся эта проблема жирной версус тонкой модели кажется мне немного надуманной. У ООП есть одна фишка, к-я там вскользь упомянута, называется инкапсуляция. Инкапсуляция означает, что клиентскому коду должно быть насрать, что там конкретно происходит, когда вызывается какой-нибудь Order.Save.
Это категорическое заблуждение. Проблема ООП именно в том, что велик риск сделать неудачную инкапсуляцию.
Дело в том, что если клиентский код обращается, к примеру, к Order.Items, то автору этого кода в первый момент могут показаться неинтересными подробности того, что там происходит "за кадром".
Но во второй момент, автор кода обнаружит, что есть принципиальная разница между предварительно загруженными и доступными Order.Items, и спрятанным "благодаря инкапсуляции" обращением к SQL серверу за наполнением этой коллекции.

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

Проблема — в том, что обращение Order.Items прячет от клиента важную семантику.

К примеру, все коннекторы к СУБД, применяемые в дотнете, поддерживают асинхронную модель исполнения. Это означает, что умный клиентский код может запросить недостающие данные, и пока они готовятся, заниматься своими делами.

В применении к Order.Items это означает, что эта "инкапсуляция" вынуждает клиентский код выполнять ряд запросов последовательно, блокируясь на время ожидания. Что, естественно, ухудшает производительность.

Далее, у клиентского кода, который делает вызов Order.Items нет никакого способа сообщить инфраструктуре подробности своих пожеланий. Он не может отменить запрос, если он занял больше времени, чем ожидалось. Он не может внятно объяснить, важно ли ему обеспечить транзакционную целостность между Order и Items, или можно отдать текущее состояние Items, а не то, которое было в момент загрузки Order. Он не может объяснить, что уже однажды загружал Items для этого Order, и теперь ему нужно обновить их, причем только в том случае, если хоть что-то поменялось.

Все эти полезные подробности, вытекающие из удаленного характера Items, скрыты за тупым геттером.
Некоторые из них можно получить, подергав за неочевидные настройки какого-то контекста. Изучение этих неочевидных настроек и составляет квалификацию опытного разработчика под Full-blown ORM. При этом некоторые из возможностей вообще остаются неизвестными даже для самых продвинутых разработчиков — к примеру, асинхронная модель исполнения.

Поэтому, продолжая твою терминологию, если клиентскому коду "насрать", то гарантированно получится "говно".
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: роль ООП при работе с данными
От: Воронков Василий Россия  
Дата: 24.11.08 11:31
Оценка: +2 -1
Здравствуйте, Sinclair, Вы писали:

ВВ>>Да и вообще, честно, вся эта проблема жирной версус тонкой модели кажется мне немного надуманной. У ООП есть одна фишка, к-я там вскользь упомянута, называется инкапсуляция. Инкапсуляция означает, что клиентскому коду должно быть насрать, что там конкретно происходит, когда вызывается какой-нибудь Order.Save.

S>Это категорическое заблуждение. Проблема ООП именно в том, что велик риск сделать неудачную инкапсуляцию.

А еще велик риск сделать неудачную декомпозицию на сущности. Неудачную иерархию наследования и пр. Везде сплошные риски.
Я вообще-то комментаривал статью, в которой критика жирной модели строится по принципу — а вот смотрите, если мы напишем это мега-криво, то, блин, это ведь действительно будет криво.
С какого боку я должен внутри какого-нибудь Order.Save вхреначить разранородный код сериализации в ХМЛ, записи в БД и прочая — да еще сделать так, чтобы одно другому мешало? Почитав автора блога можно сделать вывод, что это особенности жирной модели. В действительности это не так.
Я привел пример реализации которая не обладает большей частью упомянутых в статье недостатков и позволяет легко модицифицировать алгоритмы, без какого-то там влияния одного на другое.

S>Дело в том, что если клиентский код обращается, к примеру, к Order.Items, то автору этого кода в первый момент могут показаться неинтересными подробности того, что там происходит "за кадром".

S>Но во второй момент, автор кода обнаружит, что есть принципиальная разница между предварительно загруженными и доступными Order.Items, и спрятанным "благодаря инкапсуляции" обращением к SQL серверу за наполнением этой коллекции.
S>Дело даже не в том, сколько времени займет эта операция. Проблемы стоимости операций и сигнатур методов обсуждались еще у Страуструпа, в доисторические времена.
S>Проблема — в том, что обращение Order.Items прячет от клиента важную семантику.

Т.е. сейчас речь уже идет о том, что ленивая загрузка в Order.Items — это плохо? Или что? Я где-то писал, что необходимо делать ленивую загрузку?
Ленивая загрузка или нужна или не нужна. Никто не заставляет "прятать" от клиента важную семантику при проектировании жирной модели. И при тонкой модели тоже можно много чего запрятать, было бы желание.
Если нужна коллекция Items — возвращаем класс Order, в котором она содержится. Если не нужна — класс OrderFrame, в котором ее нет.
Также как и автор статьи по ссылке, ты пытаешь жирной модели приписать недостатки, которых у нее нет. Это недостатки конкретного подхода к проектированию жирной модели.

S>К примеру, все коннекторы к СУБД, применяемые в дотнете, поддерживают асинхронную модель исполнения. Это означает, что умный клиентский код может запросить недостающие данные, и пока они готовятся, заниматься своими делами.

S>В применении к Order.Items это означает, что эта "инкапсуляция" вынуждает клиентский код выполнять ряд запросов последовательно, блокируясь на время ожидания. Что, естественно, ухудшает производительность.

А что хочет клиентский код? Получить синхронно Order и асинхронно какие-то внутренние Items? Кто мешает реализовать это в жирной модели? И почему жирная модель должна полагаться на какие-то непонятно ленивые св-ва типа Items? Например, класс Order может предоставлять метол для асинхронной загрузки своих данных на основе данных OrderFrame.

И заметь, я про это ничего не писал. Я сказал, что клиентскому коду должно быть насрать на то, что происходит при вызове Order.Save — выполняется ли код внутри Order, делегирует ли вызов и пр. Это утверждение было моментально перенесено на что-то другое, с чем можно удобно поспорить. Не конструктивно, товарищ.

S>Далее, у клиентского кода, который делает вызов Order.Items нет никакого способа сообщить инфраструктуре подробности своих пожеланий. Он не может отменить запрос, если он занял больше времени, чем ожидалось. Он не может внятно объяснить, важно ли ему обеспечить транзакционную целостность между Order и Items, или можно отдать текущее состояние Items, а не то, которое было в момент загрузки Order. Он не может объяснить, что уже однажды загружал Items для этого Order, и теперь ему нужно обновить их, причем только в том случае, если хоть что-то поменялось.

S>Все эти полезные подробности, вытекающие из удаленного характера Items, скрыты за тупым геттером.

Пишите тривиальную реализацию свойств, и все будет прекрасно.

S>Некоторые из них можно получить, подергав за неочевидные настройки какого-то контекста. Изучение этих неочевидных настроек и составляет квалификацию опытного разработчика под Full-blown ORM. При этом некоторые из возможностей вообще остаются неизвестными даже для самых продвинутых разработчиков — к примеру, асинхронная модель исполнения.

S>Поэтому, продолжая твою терминологию, если клиентскому коду "насрать", то гарантированно получится "говно".

Да, очередная попытка вырвать фразу из контекста и сделать на основе нее далеко идущие выводы. Низачет.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Re[4]: роль ООП при работе с данными
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.11.08 12:02
Оценка: 1 (1) +1
Здравствуйте, Воронков Василий, Вы писали:

ВВ>А еще велик риск сделать неудачную декомпозицию на сущности. Неудачную иерархию наследования и пр. Везде сплошные риски.

ВВ>Я вообще-то комментаривал статью, в которой критика жирной модели строится по принципу — а вот смотрите, если мы напишем это мега-криво, то, блин, это ведь действительно будет криво.

ВВ>С какого боку я должен внутри какого-нибудь Order.Save вхреначить разранородный код сериализации в ХМЛ, записи в БД и прочая — да еще сделать так, чтобы одно другому мешало? Почитав автора блога можно сделать вывод, что это особенности жирной модели. В действительности это не так.

Прочитав автора блога, можно сделать вывод, что что бы ты ни вхреначил в метод Order.Save — это будет криво.
ВВ>Я привел пример реализации которая не обладает большей частью упомянутых в статье недостатков и позволяет легко модицифицировать алгоритмы, без какого-то там влияния одного на другое.
Да ну правда что ли? Приведенный тобой пример ровно и обладает "большей частью упомянутых в статье недостатков".
В частности, Order начинает зависеть от IPersistenceModel. Я уж молчу про то, что в твоем коде как-то ловко сделан переход от типа к экземпляру, то есть механика скармливания в Order конкретного экземпляра Database почему-то оставлена за кадром.

ВВ>Т.е. сейчас речь уже идет о том, что ленивая загрузка в Order.Items — это плохо?

Да.
ВВ>Или что? Я где-то писал, что необходимо делать ленивую загрузку?

ВВ>Если нужна коллекция Items — возвращаем класс Order, в котором она содержится. Если не нужна — класс OrderFrame, в котором ее нет.

Ты приводишь вырожденный пример, где есть два класса, причем вроде бы в тонкой модели. Фишка в том, что в жизни их будет не 2, а 2-в-степени-количество-ленивых-свойств.

ВВ>А что хочет клиентский код? Получить синхронно Order и асинхронно какие-то внутренние Items? Кто мешает реализовать это в жирной модели?

Никто не мешает. Но в жирной модели мы перемешиваем логику управления загрузкой с бизнес-логикой.
ВВ>И почему жирная модель должна полагаться на какие-то непонятно ленивые св-ва типа Items? Например, класс Order может предоставлять метол для асинхронной загрузки своих данных на основе данных OrderFrame.
О, это уже вообще запредельный ахтунг.

ВВ>И заметь, я про это ничего не писал. Я сказал, что клиентскому коду должно быть насрать на то, что происходит при вызове Order.Save — выполняется ли код внутри Order, делегирует ли вызов и пр. Это утверждение было моментально перенесено на что-то другое, с чем можно удобно поспорить.

Ок, это утверждение тоже, мягко говоря, неверно.
Потому, что у клиентского кода должна быть, к примеру, некоторая уверенность в том, что Order.Save запишет ордер туда же, куда Customer.Save только что записала кастомера.
Инкапсуляция здесь только вредит — какова семантика этого Save? В контексте какой транзакции будут проведены изменения? Почему объект доменной модели обязан знать про транзакции и контексты? Зачем нам вообще всовывать во все доменные объекты Save, который еще и должен быть реализован одинаковым образом — делегировать вызов в соответствующий сервис? Это чему помогает? Если сильно чешется писать именно "горшочек.Вари()" — используйте Extension Methods.

ВВ>Пишите тривиальную реализацию свойств, и все будет прекрасно.

То есть применять анемичную модель? Я — согласен.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: роль ООП при работе с данными
От: Aikin Беларусь kavaleu.ru
Дата: 24.11.08 12:20
Оценка:
Здравствуйте, Sinclair, Вы писали:

ВВ>>А что хочет клиентский код? Получить синхронно Order и асинхронно какие-то внутренние Items? Кто мешает реализовать это в жирной модели?

S>В частности, Order начинает зависеть от IPersistenceModel. Я уж молчу про то, что в твоем коде как-то ловко сделан переход от типа к экземпляру, то есть механика скармливания в Order конкретного экземпляра Database почему-то оставлена за кадром.
S>Никто не мешает. Но в жирной модели мы перемешиваем логику управления загрузкой с бизнес-логикой.
Никто ничего не перемешивает. Вполне возможно совместить Persistance Ignorance и Lasy Load -- проксирование ведь никто не отменял. Я даже как-то ваял пример
Автор: Aikin
Дата: 23.05.08
.

ВВ>>И почему жирная модель должна полагаться на какие-то непонятно ленивые св-ва типа Items? Например, класс Order может предоставлять метол для асинхронной загрузки своих данных на основе данных OrderFrame.

S>О, это уже вообще запредельный ахтунг.
+1
Re[5]: роль ООП при работе с данными
От: Воронков Василий Россия  
Дата: 24.11.08 12:39
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Прочитав автора блога, можно сделать вывод, что что бы ты ни вхреначил в метод Order.Save — это будет криво.

ВВ>>Я привел пример реализации которая не обладает большей частью упомянутых в статье недостатков и позволяет легко модицифицировать алгоритмы, без какого-то там влияния одного на другое.
S>Да ну правда что ли? Приведенный тобой пример ровно и обладает "большей частью упомянутых в статье недостатков".

Какими конкретно? Единственный недостаток — проблемы в распределенном приложении, в котором, собственно, говоря чем тоньще интерфейс тем лучше.

S>В частности, Order начинает зависеть от IPersistenceModel.


Будет. На то это и жирная модель. Причем никто не заставляет выносить IPersistenceModel в интерфейс, если это не нужно.
Вот только модификация алгоритмов будет происходить также легко как и в случае, что с тонкой моделью — что ты сам так долго расписывал в качестве критики жирной модели.

S>Я уж молчу про то, что в твоем коде как-то ловко сделан переход от типа к экземпляру, то есть механика скармливания в Order конкретного экземпляра Database почему-то оставлена за кадром.


Перед от "типа к экземпляру" — деталь реализации, неважная в данном случае.

ВВ>>Т.е. сейчас речь уже идет о том, что ленивая загрузка в Order.Items — это плохо?

S>Да.
ВВ>>Или что? Я где-то писал, что необходимо делать ленивую загрузку?
ВВ>>Если нужна коллекция Items — возвращаем класс Order, в котором она содержится. Если не нужна — класс OrderFrame, в котором ее нет.
S>Ты приводишь вырожденный пример, где есть два класса, причем вроде бы в тонкой модели. Фишка в том, что в жизни их будет не 2, а 2-в-степени-количество-ленивых-свойств.

А сколько обычно ленивых свойств бывает в жирной модели? 100, 1000? Значит будет столько и классов. Каждое ленивое свойство прячет за собой фактически связь между сущностями. И логично, что такая связь должна быть представлена в виде сущности.
А заодно стоит отличать пример и собственно описание подхода, которое за этим примером кроется. Если связей много, кто мешает сделать так:

class OneToManyRelation<TParent,TChild>
{
    TParent Parent { get; }
    
    List<TChild> Children { get; }
}


Да и если даже наваять тысячи классов, то вся проблема будет только в количестве классов. Других проблем не будет.

ВВ>>А что хочет клиентский код? Получить синхронно Order и асинхронно какие-то внутренние Items? Кто мешает реализовать это в жирной модели?

S>Никто не мешает. Но в жирной модели мы перемешиваем логику управления загрузкой с бизнес-логикой.

А ты не перемешивай. И причем тут вообще жирная модель? В тонкой модели в каком-нибудь OrderService ты можешь с таким же успехом перешивать бизнес-логику с логикой загрузки.

ВВ>>И почему жирная модель должна полагаться на какие-то непонятно ленивые св-ва типа Items? Например, класс Order может предоставлять метол для асинхронной загрузки своих данных на основе данных OrderFrame.

S>О, это уже вообще запредельный ахтунг.

Если развить пример, то я не вижу никакого ахтунга:

OneToManyRelation<TParent,TChild> CreateOneToManyRelation<Order,OrderItem>(myOrder order);

ВВ>>И заметь, я про это ничего не писал. Я сказал, что клиентскому коду должно быть насрать на то, что происходит при вызове Order.Save — выполняется ли код внутри Order, делегирует ли вызов и пр. Это утверждение было моментально перенесено на что-то другое, с чем можно удобно поспорить.

S>Ок, это утверждение тоже, мягко говоря, неверно.

Неверно то, что клиентский код не должен знать как именно происходит сохранение?

S>Потому, что у клиентского кода должна быть, к примеру, некоторая уверенность в том, что Order.Save запишет ордер туда же, куда Customer.Save только что записала кастомера.


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

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

S>Инкапсуляция здесь только вредит — какова семантика этого Save?


Такая же как и у OrderService.Save

S>В контексте какой транзакции будут проведены изменения?


А клиент в курсе про транзакции? Т.е. если завтра мы переделает персистенции с базы на ХМЛ — клиентский код в recycle bin?

S>Почему объект доменной модели обязан знать про транзакции и контексты?


Объект не знает. Знает IPersistanceModel.

S>Зачем нам вообще всовывать во все доменные объекты Save, который еще и должен быть реализован одинаковым образом — делегировать вызов в соответствующий сервис?


Это более "объектно ориентированно"
Я кстати нигде не устраивал противопоставление жирная vs. тонкая. Я лишь показываю, что критика жирной модели, которая приводится в блоге, по большей части несостоятельна.
Недостаток у жирной модели — один. Это жирный интерфейс.
У тонкой модель единственный плюс по сравнению с жирной моделью — это тонкий интерфейс.
И наоборот.
А то "клиентскому коду насрать", к которому ты так прицепился, относится именно к тому, что у нас жирная модель может быть построена по всем правилам тонкой модели вообще-то, с отдельными сервисами бизнес-логики, ДАЛа и проч. Бизнес-сущность будет лишь делать делегирование. При этом клиентский код будет в принципе не знать, как конкретно осуществляется эта операция — напрямую через бизнес-сущность или через отдельный сервис. И это действительно не должно касаться клиентского кода.

S>Это чему помогает?


Это чему-то мешает?

S>Если сильно чешется писать именно "горшочек.Вари()" — используйте Extension Methods.


Это часть контракта, зачем нужен extension methods.
Хотя... По большому счету это другой способ описания контракта.
Вот если оформить всю ф-ть в виде extension methods — это будет жирная или тонкая модель? Фактически — тонкая. Работать с ней клиент будет как с жирной. И соотв. прихожим к моему первоначальному утверждению, что вся эта проблема жирной версус тонкой модели является несколько надуманной.

ВВ>>Пишите тривиальную реализацию свойств, и все будет прекрасно.

S>То есть применять анемичную модель? Я — согласен.

Т.е. писать функции, которые предполагают однозначные действия. Если класс OrderService вместо очевидного Save будет содерждать метод FuckMySister — то вроде бы все у нас останется в рамках тонкой модели, однако вот смысл этого метода в отношении Order-a будет весьма и весьма туманен.
Или для тебя тривиальная реализация св-в равнозначна малохольной модели?
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Re[6]: роль ООП при работе с данными
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.11.08 13:18
Оценка: +1 -2
Здравствуйте, Воронков Василий, Вы писали:


S>>В частности, Order начинает зависеть от IPersistenceModel.


ВВ>Будет. На то это и жирная модель. Причем никто не заставляет выносить IPersistenceModel в интерфейс, если это не нужно.

И? Проблемы-то как раз в том, что невозможно вынести реализацию Order никуда, где нету конкретной PersistenceModel. Высокая связность, всё такое.

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

Не вижу никакой лёгкости. Где пример смены механизма Persistence? Где пример, когда один и тот же Order читается из Database, а сериализуется в XML?

ВВ>Перед от "типа к экземпляру" — деталь реализации, неважная в данном случае.

Ну конечно. Грязными подробностями лучше сразу пренебречь.

S>>Ты приводишь вырожденный пример, где есть два класса, причем вроде бы в тонкой модели. Фишка в том, что в жизни их будет не 2, а 2-в-степени-количество-ленивых-свойств.


ВВ>А сколько обычно ленивых свойств бывает в жирной модели?

Обычно — штук десять.
ВВ>100, 1000? Значит будет столько и классов. Каждое ленивое свойство прячет за собой фактически связь между сущностями. И логично, что такая связь должна быть представлена в виде сущности.
Ты ничего не путаешь? Это будет 2^10 классов, то есть 1024. Не многовато ли?

ВВ>А заодно стоит отличать пример и собственно описание подхода, которое за этим примером кроется. Если связей много, кто мешает сделать так:


ВВ>
ВВ>class OneToManyRelation<TParent,TChild>
ВВ>{
ВВ>    TParent Parent { get; }
    
ВВ>    List<TChild> Children { get; }
ВВ>}
ВВ>

Отлично. Давайте теперь откажемся от типизации. Вместо Order.Client, Order.Manager и так далее у нас будет итерирование по Children.
Сomedy Club отдыхает.

ВВ>Да и если даже наваять тысячи классов, то вся проблема будет только в количестве классов. Других проблем не будет.

Да уж, майнтенанс тысячи классов — это пренебрежимо малая проблема по сравнению с преимуществами толстой модели.

ВВ>А ты не перемешивай. И причем тут вообще жирная модель? В тонкой модели в каком-нибудь OrderService ты можешь с таким же успехом перешивать бизнес-логику с логикой загрузки.

А могу и не перемешивать. А у тебя — 100% перемешивание.

ВВ>Если развить пример, то я не вижу никакого ахтунга:

ВВ>OneToManyRelation<TParent,TChild> CreateOneToManyRelation<Order,OrderItem>(myOrder order);
Ахтунг продолжает нарастать. Это где у нас предполагается такой метод?

ВВ>Неверно то, что клиентский код не должен знать как именно происходит сохранение?

Неверно то, что клиентский код должен рассчитывать на волшебную сохраняемость ордера как объекта.

ВВ>Такая уверенность не исключает инкапсуляции.

Просто место для этой инкапсуляции нужно выбирать с умом.

ВВ>Кстати, может, стоить прояснять смысл последнего термина, а то, мне кажется, есть некоторое непонимание?

ВВ>Инкапсуляция предполагает скрытие деталей реализации, а не самого действия. При вызове Save клиентский код должен знать что именно и куда сохраняется, но ему должно быть по фиг как именно это происходит.
Совершенно верно. И мне совершенно непонятно, каким образом в предлагаемом тобой примере клиентский код понимает, куда именно мы сохраняемся!

S>>Инкапсуляция здесь только вредит — какова семантика этого Save?

ВВ>Такая же как и у OrderService.Save
Где гарантия?

S>>В контексте какой транзакции будут проведены изменения?

ВВ>А клиент в курсе про транзакции? Т.е. если завтра мы переделает персистенции с базы на ХМЛ — клиентский код в recycle bin?
Естественно в курсе. Напомню, что транзакции — часть существенной логики.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: роль ООП при работе с данными
От: Воронков Василий Россия  
Дата: 24.11.08 13:44
Оценка: -1
Здравствуйте, Sinclair, Вы писали:

ВВ>>Будет. На то это и жирная модель. Причем никто не заставляет выносить IPersistenceModel в интерфейс, если это не нужно.

S>И? Проблемы-то как раз в том, что невозможно вынести реализацию Order никуда, где нету конкретной PersistenceModel. Высокая связность, всё такое.

А куда ее надо вынести? Преимущества легкой модели при распределенке — очевидны. Никто опять-таки не мешает, как уже было сказано, не выносить persistance model в интерфейс, что в случае с распределенкой только помешает и корректно описать ситуацию ее отсутствия.
Если нет конретной PersistenceModel, то это, наверное, значит, что нам нужны "тупо данные", не так ли? Это в любом случае не повод прикручивать ленивую инициализацию и пр., как это описывается в статье. Можно и вообще сделать так:

Order<T> : Order where T : IPersistanceModel

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

S>Не вижу никакой лёгкости. Где пример смены механизма Persistence?

А что по-твоему может вызвать проблему при смене Persistance. Сама сущность под конкретный persistance никак не заточена.

S>Где пример, когда один и тот же Order читается из Database, а сериализуется в XML?


А как эта проблема будет решаться в случае с тонкой моделью?
Ты пытаешься придумать для жирной модели проблемы, которых нет. Вернее, они есть в любой модели.
В данном конкретном случае — гибридная PersistanceModel.

ВВ>>Перед от "типа к экземпляру" — деталь реализации, неважная в данном случае.

S>Ну конечно. Грязными подробностями лучше сразу пренебречь.

И что в них грязного?
"Подробности" будут абсолютно такие же как и в случае с тонкой моделью — с той лишь разницей, что там вызывает метод GetOrder, здесь — конструктор класса.

S>>>Ты приводишь вырожденный пример, где есть два класса, причем вроде бы в тонкой модели. Фишка в том, что в жизни их будет не 2, а 2-в-степени-количество-ленивых-свойств.

ВВ>>А сколько обычно ленивых свойств бывает в жирной модели?
S>Обычно — штук десять.
ВВ>>100, 1000? Значит будет столько и классов. Каждое ленивое свойство прячет за собой фактически связь между сущностями. И логично, что такая связь должна быть представлена в виде сущности.
S>Ты ничего не путаешь? Это будет 2^10 классов, то есть 1024. Не многовато ли?

Я вообще-то имел в виду всего. Но ты видно опять нашел способ повеселиться.

ВВ>>А заодно стоит отличать пример и собственно описание подхода, которое за этим примером кроется. Если связей много, кто мешает сделать так:

ВВ>>
ВВ>>class OneToManyRelation<TParent,TChild>
ВВ>>{
ВВ>>    TParent Parent { get; }
ВВ>>    List<TChild> Children { get; }
ВВ>>}
ВВ>>

S>Отлично. Давайте теперь откажемся от типизации. Вместо Order.Client, Order.Manager и так далее у нас будет итерирование по Children.
S>Сomedy Club отдыхает.

Да, у тебя наверное точно будет "итерирование по Children".
Где тут отказ от типизации? Не вижу никакой проблемы в том, чтобы представить связь между сущностями в виде отдельной сущности, если так будет удобнее в конкретном случае.

ВВ>>А ты не перемешивай. И причем тут вообще жирная модель? В тонкой модели в каком-нибудь OrderService ты можешь с таким же успехом перешивать бизнес-логику с логикой загрузки.

S>А могу и не перемешивать. А у тебя — 100% перемешивание.

Обосновать можешь? У меня ровно те же самые возможности для разделения слоев, что и в тонкой модели.

ВВ>>Если развить пример, то я не вижу никакого ахтунга:

ВВ>>OneToManyRelation<TParent,TChild> CreateOneToManyRelation<Order,OrderItem>(myOrder order);
S>Ахтунг продолжает нарастать. Это где у нас предполагается такой метод?

Ну положим, есть класс Customer, у него есть отношение один-ко-многим к Order. Соответственно, мы имеем:

сам класс Customer, содержащий только данные по Customer и методы для работы только с данными Customer.
класс CustomerOrder —

CustomerOrderList CustomerOrder.GetOrdersFor(Customer customer)

— содержащий данные по взаимосвязи Customer->Order.

ВВ>>Неверно то, что клиентский код не должен знать как именно происходит сохранение?

S>Неверно то, что клиентский код должен рассчитывать на волшебную сохраняемость ордера как объекта.
ВВ>>Такая уверенность не исключает инкапсуляции.
S>Просто место для этой инкапсуляции нужно выбирать с умом.

Отлично, аргументы подменяем пустой демагогией.

ВВ>>Кстати, может, стоить прояснять смысл последнего термина, а то, мне кажется, есть некоторое непонимание?

ВВ>>Инкапсуляция предполагает скрытие деталей реализации, а не самого действия. При вызове Save клиентский код должен знать что именно и куда сохраняется, но ему должно быть по фиг как именно это происходит.
S>Совершенно верно. И мне совершенно непонятно, каким образом в предлагаемом тобой примере клиентский код понимает, куда именно мы сохраняемся!

А в случае с тонкой моделью как мы это понимаем?
Здесь — точно также.

S>>>Инкапсуляция здесь только вредит — какова семантика этого Save?

ВВ>>Такая же как и у OrderService.Save
S>Где гарантия?

Где гарантия, что OrderService.Save будет выполнять нужные нам действия, а?

S>>>В контексте какой транзакции будут проведены изменения?

ВВ>>А клиент в курсе про транзакции? Т.е. если завтра мы переделает персистенции с базы на ХМЛ — клиентский код в recycle bin?
S>Естественно в курсе. Напомню, что транзакции — часть существенной логики.

В таком случае никто не мешает предоставить доступ к модели транзакций и через жирную модель.
В жирной модели можно сделать все то же самое, что и в тонкой модели.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.