Избегайте циклической ссылки в доменной модели
Это должен быть такой распространенный сценарий, что об этом уже много написано, надеюсь, даже действительно хороший шаблон. У меня есть модель домена, в которой пользовательский контейнер содержит объекты. Например (свойства и интерфейсы исключены для краткости):
class Entity
{
public int Id;
public EntityContainer ParentContainer;
}
class EntityContainer
{
public int Id;
public IList<Entity> Entities = new List<Entity>();
public void AddEntity(Entity entity)
{
entity.ParentContainer = this;
Entities.Add(entity);
}
}
class Main
{
public Main()
{
Entity entity1 = new Entity();
Entity entity2 = new Entity();
EntityContainer entityContainer = new EntityContainer();
entityContainer.AddEntity(entity1);
entityContainer.AddEntity(entity2);
// Can now traverse graph easily, e.g.
Console.WriteLine("entity1's parent container ID = " + entity1.ParentContainer.Id);
Console.WriteLine("Container contains at least this entity ID: " + entityContainer.Entities[0].Id);
}
}
Теперь я могу легко пройти мой граф объектов в обоих направлениях, но создал круговую ссылку. Вы бы создали третий тип, чтобы развести зависимости?
заранее спасибо
4 ответа
Нет ничего плохого в циклических ссылках как таковых, и они широко используются в.NET Framework, например, XmlNode.OwnerDocument, Control.Parent.
Если вам нужно пройти вверх по дереву, тогда лучше использовать обратную ссылку.
В COM циклические ссылки сложны, потому что если вы установили контейнер и все его дочерние элементы в ничто, то объекты не будут очищены должным образом, так как дочерние элементы все еще содержат ссылки на родительский объект. Однако сборка мусора.NET не имеет проблем с тем, как он реализован.
Должен ли контейнер знать о типе содержимого? Если нет, то дженерики могут этого избежать - т.е. Container<T>
где вы случайно используете Container<Entity>
, Кроме этого; вставка необходимых деталей в интерфейс (или базовый класс) в сборке, на которую могут ссылаться оба, является распространенным подходом.
Лично я постараюсь просто избежать необходимости, чтобы ребенок знал о родителе.
Также; обратите внимание, что если вы идете по маршруту абстракции (интерфейс и т. д.); это может иметь большое значение, если вы используете (например) сериализацию xml.
(редактировать комментарии) ОК; во-первых: что является причиной циклической ссылки (в сборке); если нет, оставьте это в покое. Если есть проблема, вам понадобится дополнительный тип; предположительно некоторые интерфейсы для представления конкретных типов - т.е. где Entity : IEntity
, а также EntityContainer
знает только о IEntity
(или В.В. с IEntityContainer
, или оба),
Поэтому я не вижу проблем с моделью вашего класса, но вы легко могли бы сделать чистый разрез. Заставьте Entity реализовать интерфейс IEntity и сделайте так, чтобы EntityContainer содержал IList, и, если у вас нет особых причин для использования IList, вы должны учитывать IEnumerable. Это облегчит работу потребителя используемого вами EntityClass. Так как передача любого массива IEntity или выражения linq будет возможна при выборе IEntities
Использование объектов с круговыми ссылками - это нормально в моей книге, при условии, что вы используете какой-то ленивый шаблон загрузки при проектировании этих объектов.
например
Вы хотите получить доступ: Company.Employee И в другом сценарии: Employee.Company
Который делает круговую ссылку, то есть Company.Employee.Company.Employee и т. Д.
Если эти свойства загружены не лениво, например, объект Company всегда загружает свое свойство Employee, а объект Employee всегда загружает его свойство Company, тогда он не будет работать слишком хорошо, когда вы вводите источник данных.