Избегайте циклической ссылки в доменной модели - PullRequest
7 голосов
/ 28 мая 2009

это, должно быть, такой распространенный сценарий, что о нем уже много написано, надеюсь, даже действительно хороший шаблон У меня есть модель домена, в которой пользовательский контейнер содержит объекты. Например (свойства и интерфейсы для краткости исключены):

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 ]

4 голосов
/ 28 мая 2009

Нет ничего плохого в циклических ссылках как таковых, и они широко используются в .NET Framework, например XmlNode.OwnerDocument, Control.Parent.

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

В COM циклические ссылки сложны, потому что, если вы установили контейнер и все его дочерние элементы в ничто, объекты не будут очищены должным образом, так как дочерние элементы все еще содержат ссылки на родительский элемент. Однако сборка мусора в .NET не имеет проблем с тем, как она реализована.

2 голосов
/ 28 мая 2009

Нужно ли контейнеру знать о типе содержимого? Если нет, то дженерики могут этого избежать - то есть Container<T>, где вы можете использовать для использования Container<Entity>. Кроме этого; вставка необходимых деталей в интерфейс (или базовый класс) в сборке, на которую могут ссылаться оба, является обычным подходом.

Лично я бы старался просто избегать того, чтобы ребенок знал о родителе.

Также; обратите внимание, что если вы do идете по маршруту абстракции (интерфейс и т. д.); это может иметь большое значение, если вы используете (например) сериализацию xml.


(редактировать комментарии) ХОРОШО; во-первых: что является причиной циклической ссылки (в сборке); если нет, оставьте это в покое. Если есть проблема, вам понадобится дополнительный тип; предположительно некоторые интерфейсы для представления конкретных типов - то есть где Entity : IEntity и EntityContainer знают только о IEntity (или vv с IEntityContainer, или обоими),

1 голос
/ 03 июня 2010

Использование объектов с круговыми ссылками в моей книге - это нормально, если вы используете какой-то шаблон с ленивой загрузкой при проектировании этих объектов.

например.

Вы хотите получить доступ: Company.Employee А в другом сценарии: Сотрудник. Компания

Что делает круговую ссылку, т.е. Company.Employee.Company.Employee и т. Д.

Если эти свойства не загружены ленивыми, например Объект Company всегда загружает его свойство Employee, а объект Employee всегда загружает его свойство Company, тогда он не будет работать слишком хорошо, когда вы вводите источник данных.

1 голос
/ 28 мая 2009

Как так, я не вижу проблемы с моделью вашего класса, но вы легко могли бы сделать чистый срез. Сделайте так, чтобы Entity реализовал интерфейс IEntity и заставил EntityContainer хранить IList, и если у вас нет особых причин для использования IList, вам следует учитывать IEnumerable. Это облегчит работу потребителя используемого вами EntityClass. С момента передачи любого массива IEntity или выражения linq можно выбрать IEntities

...