Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I have a class Container containing objects of type Item. They are different classes, and especially they have no common base class (e.g. a Container does not itself have a Container).:

class Container
{
    public ICollection<Item> Items { get { /* ... */ } }
}

class Item
{
    public Container Container { get { /* ... */ } }
}

I’d like to enforce that each Item needs to have exactly one Container, and I’d like to make sure there never can be an inconsistent situation such that e.g. i.Container.Items.Contains(i) returns false.

This could be done by e.g. adding a method Container.AddItem that sets the Item’s Container property accordingly or by having a Item.SetContainer method that takes care of Container.Items.

Example:

class Item
{
    private Container container;
    public Container Container
    {
       get
       {
          return container;
       }
       set
       {
           if (container != null)
               container.Items.Remove(this);
           container = value;
           if (value != null)
               value.Items.Add(this);
       }
    }
}

This could be easily done with the concept of a “friend”, but I’m using C# that does not have friends, and thus this example code won’t compile. However, it of course would compile if Container.Items had a public setter, but this again would allow inconsistencies: A “user” of the classes would need to know not to use the setter of Container.Items, but Item.Container instead.

After searching for quite some time, I feel there is no way to enforce this restriction that seems to be so trivial. (Using internal might be a work-around, but no solution.)

Is there an elegant way to enforce this one-container-per-item rule without the concept of “friend”?

share|improve this question
    
Do you want to only allow Container to have access to Item.Container or what is your problem? –  hansmaad Apr 4 at 9:35
1  
Can't you just pass the container in the constructor? –  AndyBursh Apr 4 at 9:48
    
@hansmaad: I’d like to avoid inconsistent situations like i.Container == c and !c.Items.Contains(i). So, yes, one solution (that is not possible without friends) is that only Container has write access to Item.Container –  Martin Apr 4 at 10:49
    
@AndyBursh: It must be possible to change an Items Container. –  Martin Apr 4 at 10:51
    
@Martin OK, so pass it in the constructor and add a setter which checks for nulls? –  AndyBursh Apr 4 at 10:53

2 Answers 2

up vote 3 down vote accepted

You could extract this to a separated service that has the only resposnsibility to track Container<->Item associoations. I think this is a very flexible solution.

class Container<T>
{
    private IItemAssociations associations;
    public Container(IItemAssociations a)
    {
        this.associations = a;
    }

    public void Add(T i)
    {
        if (!associations.IsAddedToAContainer(i))
        {
            DoAdd(i);
            associations.AddAssociation(this, i);
        }
        else
            HandleAlreadyAdded(i);
    }
}

You can have a static constructor method that injects a global association object or use some other injection mechanism.

share|improve this answer

internal would be a fine solution here since container and item are inextricably linked, they're going to be part of the same assembly.

That said, this is a code smell.

Items should generally not know if they belong in a container, let alone being restrained to only one container. This is a coupling nightmare since items will then inevitably go back to their container to look for other items, tightening that relation. Then you get into "how do I inform this item that the peer it cared about has suddenly left the container?" Which makes the container then care about which items care about what other items and on down.

share|improve this answer
    
Maybe it is a code smell. Consider System.Windows.Forms.Control: It has a Controls and a Parent property. This is somewhat similar, although it is the same class, and not two different classes. –  Martin Apr 4 at 11:51
1  
@Martin - That's why I said code smell rather than anti-pattern. It can certainly be the cleanest way to accomplish a task, especially location sort of containers rather than parent sort of things. But I'd still avoid it if possible. As soon as a child knows about its parent, it's no longer a child but a peer. –  Telastyn Apr 4 at 13:28
    
I think this is an interesting aspect. –  Martin Apr 4 at 14:51

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.