В последнее время я много думал об объектно-ориентированных принципах/практиках/парадигмах, таких как SOLID, Закон Деметры и DDD, и тема, которая продолжает всплывать на поверхность, - это обеспечение кардинальности объектов.
Мощность ассоциации объектов определяется бизнес-правилами, согласно которым определенные сущности могут быть связаны только с определенным числом других объектов-сущностей. Например, если вы разрабатываете систему для управления складами, бизнес-правило может заключаться в том, что один товар может храниться только на одном складе. Очевидно, что соблюдение этих правил при разработке программного обеспечения является вопросом реализации.
Что мне интересно, так это: в случае, если бизнес-сфера требует жесткой кардинальной модели, каков наилучший способ обеспечить ее соблюдение? Методы, которые я могу придумать навскидку, включают следующее:
Двунаправленные ссылки – двунаправленные ассоциации между связанными объектами (объект A ссылается на объект B, а объект B ссылается на объект A).
class Warehouse { private List<Item> items; public void RegisterItem(Item obj) { if(obj.Warehouse != null) throw new ArgumentException("Can only register un-owned item") items.Add(obj); obj.Warehouse = this; } }
Инкапсулировать принадлежащий объект. Позвольте владельцу управлять его созданием и удалением и предоставить доступ через набор API-интерфейсов, которые абстрагируют реальную реализацию объекта (объект A клонирует объект B или создает объект B на основе пройденная схема)
class Warehouse { private List<Item> items; public void RegisterItem(Item obj) { items.Add((Item)obj.Clone()); } public void RegisterItem(ItemDescriptor item) { items.Add(new Item(item)); } }
Сторонний монитор. Попросите третью сторону, которая понимает ограничения кардинальности, создать и подключить ассоциации объектов надлежащим образом (объект C знает об отношениях между A и B и несет ответственность за их создание и поддержку — этот метод доступен только на C и недоступны для клиентов)
class Warehouse { private List<Item> items; internal void RegisterItem(Item obj) { items.Add(obj); } } class WarehouseItemRegistrationService { private List<Item> registeredItems; public void RegisterItem(Warehouse warehouse, Item item) { if(registeredItems.Contains(item)) throw new ArgumentException("Can only register un-owned items"); warehouse.RegisterItem(item); } }
Я думаю, что у каждой техники есть свои сильные и слабые стороны. Двунаправленные ассоциации могут усложнить граф объектов и потребовать использования частных API для обновления ссылок, однако их очень просто реализовать, и бизнес-ограничения встраиваются в классы бизнес-сущностей. Инкапсуляция принадлежащей сущности может усложнить модель предметной области, заставив сущности иметь описание, основанное на значении, но это очень чисто. Сторонний монитор изолирует явные ограничения количества элементов в отдельный класс, но также усложняет модель предметной области.
Есть ли у кого-нибудь другие мысли, идеи или лучшие подходы?