В недавнем выпуске подкаста DotNet&More мы обсуждали с Максимом Аршиновым его предстоящий доклад на Московский .Next "Блеск и нищета предметной модели". С позицией Максима можно будет легко познакомиться непосредственно на конференции. И, в качестве дополнения, я бы хотел рассмотреть видение великого спора Анемичная VS "Богатая" ("Насыщенная") доменные модели через призму некогда популярных GRASP шаблонов Дискуссии идут достаточно давно, например: Прежде чем начать разбор, хотелось бы прояснить предмет спора. Основное отличие анемичной модели от богатой в том, что она не содержит бизнес логику в теле класса. Частным примером анемичной модели может быть всем известный паттерн DTO. "Богатая" модель, в свою очередь, может содержать логику, описывающую бизнес правила, функции и т.д. Обратите внимание, что мы рассматриваем именно методы, отражающие бизнес логику. ToString, GetHashCode и прочие "технические" части классов не входят в предмет обсуждения, потому и игнорируются. Как и отмечалось в начале статьи, рассматривать данный вопрос мы будем в контексте GRASP паттернов. Данный набор шаблонов был представлен в книге Крэга Лармана «Применение UML и шаблонов проектирования» и сильно повлиял на современное программирование, например, правила Low Coupling/High Cohesion были объявлены именно в GRASP. Для людей, знакомых с GoF паттернами данный набор шаблонов может показаться слишком размытым. Все дело в том, что GRASP паттерны сфокусированы не на решении прикладных задач, а на распределении ответственности за те или иные действия и операции между объектами: В контексте данной статьи хотелось бы сфокусироваться на следующих паттернах: Проблема: Каков базовый принцип распределения обязанностей между объектами?
Решение: Назначить эту обязанность тому классу, который обладает достаточной информацией для ее выполнения. Другими словами: В контексте C#: метод стоит объявлять в том классе, поля и свойства которого используются в этом методе.
Например, если для расчета суммы долга вся необходимая информация есть у сущности должника (сумма, время выдачи кредита), то именно в этой сущности и стоит объявить соответствующий метод.
Конечно, данное упрощение несколько чрезмерно, но основной смысл должен быть понятен. Проблема: Какой класс должен обеспечить реализацию High Cohesion и Low Coupling, если шаблон Information Expert не обеспечивает подходящего решения.
Решение: Присвоить группу обязанностей с высокой степенью зацепления искусственному классу, не представляющему конкретного понятия предметной области. Другими словами: В контексте C#: если реализация метода предусматривает равномерное использование нескольких сущностей или внешних зависимостей, то данный метод стоит объявить в отдельном классе. И эти классы называют сервисами.
Например, если для расчета суммы долга необходимо знать не только атрибуты должника, но и параметры банка (процентная ставка, допустимый период просрочки), то стоит создать отдельный сервис, содержащий соответствующий метод. В контексте вышесказанного, можно выделить достаточно простой алгоритм, помогающий принять решение, располагать метод в модели или в сервисе: GRASP паттерны нельзя рассматривать как истину в последней инстанции. Но в они могут помочь легко понять, какому объекту назначить то или иное поведение. Соответственно, спор анемичная или "богатая" модель может не иметь смысла, так как решение принимается в каждом конкретном случае, в зависимости от модели предметной области и поведения, связанного с ней. В процессе дизайна системы будут появляться как сущности с поведением, так и без него. И это вполне корректно. Lead Software Engineer