Изменение нашего дизайна BL - какой шаблон (ы) будет наиболее полезным? - PullRequest
3 голосов
/ 22 декабря 2008

Я нахожусь в процессе рефакторинга наших уровней BI, чтобы сделать наш код более "слабосвязанным", и мне было интересно услышать, что вы, ребята, думаете, могут быть интересные улучшения, чтобы сделать?

В настоящее время наш API выглядит примерно так: -

// Fetch a collection of objects
ProductCollection prods = Product.GetProducts();

// Load an individual object, make change and save it back
Product p = new Product();
if(p.Load(productID))
{
    p.Name = "New Name";
    p.Save();
}

Как видите, все наши методы для извлечения коллекций объектов / загрузки отдельных объектов и сохранения изменений встроены в класс "Model". Каждый из наших классов Model наследует от базового класса ObjectBase, который включает в себя функции доступа к БД и отслеживание изменений, поэтому, когда кто-либо изменяет значение через свойство, объект автоматически помечается как грязный, и уведомления запускаются для любого объекта (UI), подписавшегося на эти события.

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

List<Product> prods = ProductService.GetProducts();

Product p = ProductService.GetSingleProduct(productID);
p.Name = "New Name";

ProductService.SaveProduct(p);

Этот способ кажется более сложным и затрудняет инкапсуляцию функциональности в бизнес-объектах.

Может кто-нибудь объяснить, почему это лучший способ сделать это, или, может быть, я неправильно понял концепции?

Спасибо

Джеймс

Ответы [ 3 ]

6 голосов
/ 22 декабря 2008

Ваш текущий API является реализацией Active Record Pattern . Этот шаблон работает нормально, когда объектная модель, используемая в коде, совпадает один на один с моделью базы данных. Другое преимущество состоит в том, что существуют инструменты для генерации этих классов, включая постоянный код и таблицы базы данных.

Альтернативой, которую вы предлагаете, является Шаблон репозитория. Как вы уже упоминали, реализация немного сложнее, но имеет ряд преимуществ. Поскольку вы можете реализовать любой вид инструмента ORM, вы не ограничены отображениями «один на один», но можете реализовать более сложные отображения, в которых объектная модель может отличаться от модели базы данных. Таким образом, вам не нужно навязывать объектную модель в базе данных или наоборот. Однако более сложные сопоставления, кроме одного на одном, не могут быть сгенерированы и требуют некоторого

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

Используя Шаблон репозитория, вы также отделяете модель от логики постоянства.

В обеих ситуациях возможно написать методы персистентности в общем виде, так что код персистентности является универсальным и не нужно знать о конкретном объекте, который необходимо сохранить. Это очевидно для шаблона активной записи, поскольку все эти объекты реализуют сохранение, удаление, обновление и т. Д. Для шаблона репозитория вы также можете использовать инструменты ORM, которые работают с любым объектом, так что код, подобный этому, возможен:

Repository.Save(ObjectOfAnyType);

ObjectOfAnyType может быть любым, если в инструменте ORM определено / реализовано некоторое отображение для типа объекта.

Таким образом, вы выбираете, хотите ли вы или нуждаетесь в этих преимуществах за счет небольшой дополнительной сложности. Или достаточно простоты шаблона активной записи.

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

2 голосов
/ 22 декабря 2008

Я думаю, что основной проблемой здесь является разделение интересов: почему ваш бизнес-объект должен заботиться о том, как он сохраняется? Почему ваш уровень персистентности должен заботиться о том, как ваши бизнес-объекты проверяются или применяют бизнес-логику? Разделяя их, вы четко различаете, как ваши объекты работают с точки зрения бизнеса и как они сохраняются / извлекаются из базы данных. В результате ваши бизнес-объекты становятся намного менее сложными (это хорошо), и вы можете легче использовать общие черты на уровне постоянства, чтобы уменьшить объем необходимого кода.

Один пример ситуации, в которой это действительно помогло мне, - это создание подклассов объектов Model. Когда я встраивал логику «Сохранить» в модель, я получал достаточное количество логики из базового класса BI и реализовывал виртуальный метод для фактического сохранения объекта в моем классе Model. Это работало нормально, пока моя иерархия модели BI была плоской. Он взорвался, когда хотел создать подкласс модели. В этот момент мне пришлось переопределить большую часть кода для сохранения, потому что я не мог использовать код базового класса без повторного использования кода непосредственного родителя - что я не мог сделать.

С тех пор я перешел на использование LINQ в качестве ORM, и теперь логика сохранения / извлечения находится в DataContext, а бизнес-логика реализована в частичных классах для каждого бизнес-объекта. Эти классы реализуют проверку (и другую бизнес-логику) и проверку загрузки через известный интерфейс. Интерфейс используется DataContext для выполнения операций, необходимых для обеспечения соблюдения бизнес-правил перед сохранением объекта. Теперь мои классы моделей стали более специализированными и более простыми в обслуживании. LINQ налагает некоторые ограничения на подклассы, но я могу обойти это, создавая интерфейсы или, при необходимости, переделывая мою таблицу DB для поддержки их ограничений подкласса. В общем, я обнаружил, что это огромное улучшение.

0 голосов
/ 22 декабря 2008

@ tvanfosson, @Marco Tolk Спасибо за ваши ответы, очень полезно.

Мне действительно нравится разделение проблем, которое приносит шаблон репозитория. Он добавляет больше зависимостей между объектами, но я предполагаю, что использование чего-то вроде StructureMap эффективно устраняет это как препятствие.

Однако мне все еще не нравится упрощенная концепция «Модель». Разве логика, относящаяся к объекту (например, «продукт» в приведенном выше примере), не должна содержаться внутри самого объекта?

Например, если бы я связывал свой объект продукта с редактором, я бы хотел, чтобы он изменил свойства объекта, а затем, когда пользователь нажал сохранить, продукт сохранился сам (возможно, через репозиторий). Мне не имеет смысла иметь службу между пользовательским интерфейсом и бизнес-объектом, когда бизнес-объект «знает» достаточно, чтобы сохранить себя, не передаваясь по трубам через другую службу.

...