在两个对象之间实现临时排他关系

时间:2019-05-24 11:27:37

标签: c# oop

我需要实现两个对象Owner和Item之间的关系,以便所有者拥有0..1个项目,并且每个项目一次只能由一个所有者拥有。

类定义如下:

class Item
{
}

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

问题在于,当所有者分配了一个项目时,任何其他所有者都将失去对该项目的所有权(并且其项目引用设置为null)。

请参见以下示例代码:

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 个答案:

答案 0 :(得分:1)

  

问题在于,当所有者分配了一个项目时,任何其他所有者都将失去对该项目的所有权(并且其项目引用设置为null)。

最简单的方法是反转关系(即谁存储对另一个的引用)

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

您也可以使用Owner.Id或类似有效的标识符,但答案仍然相同。

这样,每个项目只有一个“框”可以注册所有者。有效进行的任何注册都将覆盖现有所有者,这就是您想要的。

当然,由于您希望关系基本上是对称的,因此可以通过在Owner类中使用引用来实现相同的目的。哪种方法都没关系,请使用最适合您的方法(您是将项目分配给所有者,还是将所有者分配给项目?这是语义上的差异,而不是技术上的差异)

  

我需要实现两个对象Owner和Item之间的关系,以便所有者拥有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);
    }
}

注释

  • 我已经跳过了您如何对所有者进行平等检查。您很可能会检查ID值,但您的问题并没有明确说明。使用适当的相等性检查。
  • 我还跳过了owner参数的空检查。目前尚不清楚物品是否曾被放弃。如果与您的案例有关,请添加它。
  • 这要求您有权访问所有项目的列表,以便可以查找所有权。如何获得该列表很大程度上取决于您存储数据的方式,您在问题中没有提到。出于示例目的,我假设ListOfAllItems确实提供了您所说的内容。
    • 如果您不希望OwnsItem()逻辑不出现在Owner类中,则不会。从DDD的角度来看,这样做很有意义,但是您也可以将其放在单独的验证对象或存储库中。这完全取决于您的体系结构,您的问题还不足以让我最终得出结论。
  • 如果您想变得聪明,并且给两个类一个相互引用,请在每个项目尝试更新其引用项目的属性时注意无限递归。可以做到这一点,但我建议尽量避免这样做有两个原因:
    • 它减少了您必须执行的数据处理。
    • 这可以防止您的数据不一致或自相矛盾的可能性(例如,引用某项目的所有者引用了另一所有者)

答案 1 :(得分:0)

这可以在设置器中完成。在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 该项目也需要删除其所有者! < / strong>

对,如果值是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