Реализация временных исключительных отношений между двумя объектами - PullRequest
1 голос
/ 24 мая 2019

Мне нужно реализовать взаимосвязь между двумя объектами «Владелец» и «Элемент», чтобы владелец владел 0..1 элементами, и каждый элемент мог принадлежать только одному владельцу одновременно.

Определения классов:следующим образом:

class Item
{
}

class Owner
{
   public Item item { get; set; }
}

Дело в том, что, когда владельцу назначен элемент, любой другой владелец теряет право собственности на элемент (и его ссылка на элемент устанавливается на ноль).

См. Пример кода:

Owner owner1 = new Owner();
Owner owner2 = new Owner();
Item anItem = new Item();
// right now owner1.item and owner2.item are both null

owner1.item = anItem;
// owner1.item is anItem and owner2.item is null

owner2.item = anItem;
// owner1.item is null and owner2.item is anItem

Каким будет эффективный и правильный способ реализации этого поведения?

Извините, если я не использую правильную терминологию, возможноиз-за отсутствия моих знаний я не смог найти решение.

Ответы [ 2 ]

1 голос
/ 24 мая 2019

Дело в том, что когда владельцу назначен элемент, любой другой владелец теряет право собственности на этот элемент (и его ссылка на элемент устанавливается на ноль).

Самый простой способ добиться этого - инвертировать отношения (т. Е. Кто хранит ссылку на другого)

public Item
{ 
    public Owner Owner { get; set; }
}

Вы также можете использовать Owner.Id или аналогичный действительный идентификатор, но ответ остается прежним.

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

Конечно, поскольку вы хотите, чтобы отношения были в основном симметричными, вы можете достичь того же, имея ссылку в классе Owner. То, как вы это делаете, не имеет большого значения, используйте тот, который имеет для вас наибольшее значение (вы назначаете элементы владельцам или назначаете владельцев на элементы? Это семантическая разница, а не техническая)

Мне нужно реализовать отношения между двумя объектами Владелец и Предмет так, чтобы владелец владел 0..1 предметами, и каждый элемент мог принадлежать только одному владельцу за один раз.

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

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

В этом нет ничего хитрого. Это становится вопросом принудительной проверки, когда кто-то хочет добавить новое отношение Владелец-Предмет. Одним из таких примеров может быть:

public class Item
{
    public Owner Owner { get; private set }

    public void SetOwner(Owner owner)
    {
        if(!owner.OwnsItem)
        {
            this.Owner = owner;
        }
        else
        {
            //Do nothing, throw an exception, log a message, ...
        }
    }
}

public class Owner
{
    public bool OwnsItem()
    {
        return ListOfAllItems.Any(item => item.Owner == this);
    }
}

Примечания

  • Я пропустил, как вы делаете проверку на равенство для владельцев. Скорее всего, вы бы проверили значение идентификатора, но ваш вопрос не прояснил это. Используйте любую проверку на равенство.
  • Я также пропустил нулевую проверку параметра owner. Неясно, откажется ли когда-либо предмет от или нет. Добавьте его, если оно имеет отношение к вашему делу.
  • Это требует, чтобы у вас был доступ к списку всех предметов, чтобы вы могли искать владельца. То, как вы получите этот список, во многом зависит от того, как вы храните свои данные, которые вы не упоминаете в своем вопросе. Для примера я предполагаю, что ListOfAllItems дает вам именно то, что говорится.
    • Логика OwnsItem() не имеет в классе Owner, если вы этого не хотите. Это имеет смысл с точки зрения DDD, но вы также можете поместить его в отдельный объект проверки или хранилище. Все зависит от вашей архитектуры, и ваш вопрос недостаточно раскрыт, чтобы я мог на него ответить окончательно.
  • Если вы хотите быть умным и давать обоим классам ссылку друг на друга, следите за бесконечной рекурсией, когда каждый элемент пытается обновить свойство своего ссылочного элемента. Это можно сделать, но я советую по возможности избегать его по двум причинам:
    • Сокращает необходимость обработки данных.
    • Это предотвращает возможность того, что ваши данные могут быть противоречивыми или противоречащими друг другу (например, владелец, который ссылается на элемент, который ссылается на другого владельца)
0 голосов
/ 24 мая 2019

Это можно сделать в сеттере. Вам может потребоваться свойство Owner в классе Item:

В классе Item:

class Item
{
    public string Name { get; set; }
    private Owner owner = null;
    public Owner Owner
    {
        get { return owner; }
        set
        {
            owner = value;
            Console.WriteLine($"I am {Name} and I am now owned by {(owner != null ? owner.Name : "no one")}");
        }
    }
}

Теперь мы можем сделать то же самое в классе Owner:

class Owner
{
    public string Name { get; set; }
    private Item item = null;
    public Item Item
    {
        get { return item; }
        set
        {
            if (value != null)
            {
                value.Owner = this;
            }
            item = value;
            Console.WriteLine($"I am {Name} and I now own {(item != null ? item.Name : "no one")}");
        }
    }
}

Но это просто устанавливает отношение в двойном направлении! Это не отменяет владение другим объектом!

Точно, но теперь мы можем использовать item.Owner, чтобы удалить связь между старым владельцем и предметом, например:

class Owner
{
    public string Name { get; set; }
    private Item item = null;
    public Item Item
    {
        get { return item; }
        set
        {
            if (value != null)
            {
                if (value.Owner != null)
                {
                    value.Owner.Item = null;
                }
                value.Owner = this;
            }
            item = value;
            Console.WriteLine($"I am {Name} and I now own {(item != null ? item.Name : "no one")}");
        }
    }
}

Но что, если я захочу использовать owner.Item = null для этого предмета также необходимо удалить его владельца!

Правильно, мы можем использовать item.Owner для удаления отношения, если значение null

class Owner
{
    public string Name { get; set; }
    private Item item = null;
    public Item Item
    {
        get { return item; }
        set
        {
            if (value != null)
            {
                if (value.Owner != null)
                {
                    value.Owner.Item = null;
                }
                value.Owner = this;
            }
            else
            {
                if (item != null)
                {
                    item.Owner = null;
                }
            }

            item = value;
            Console.WriteLine($"I am {Name} and I now own {(item != null ? item.Name : "no one")}");
        }
    }
}

Теперь мы можем проверить это:

Owner owner1 = new Owner { Name = "owner1" };
Owner owner2 = new Owner { Name = "owner2" };
Item anItem = new Item { Name = "item1" };

owner1.Item = anItem;
Console.WriteLine("---------------------------------");
owner2.Item = anItem;

выход

I am item1 and I am now owned by owner1
I am owner1 and I now own item1
---------------------------------
I am item1 and I am now owned by no one
I am owner1 and I now own no one
I am item1 and I am now owned by owner2
I am owner2 and I now own item1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...