views:

190

answers:

7

I am not sure if there is already a nomenclature for this, but for the sake of this question lets define two terms: peer implementation or nested implementation to illustrate how you implement collection classes in a data model that contains many parent/child entity relationships.

I use the term peer to describe the scenario where you implement the collection classes in your model layer along side with the entity classes essentially making them peers in your API like so:

public class ParentEntity
{
  private ChildEntityCollection children;
}

public class ChildEntity
{
}

public class ChildEntityCollection : ICollection<ChildEntity>
{
}

The main advantage here is that you can reuse the collection class in other entity classes that happen to store children of the same type.

I use the term nested to describe the scenario where you implement them as a nested class like so:

public class ParentEntity
{
  private ChildEntityCollection children;

  public class ChildEntityCollection : ICollection<ChildEntity>
  {
  }
}

public class ChildEntity
{
}

The main advantage here is that each parent can implement their own collection class to store its children in manner that is most optimized for that specific parent. For example, one parent entity may find that an array data structure works well whereas another may use a splay tree (obscure I know, but it illustrates my point well).

I have noticed that Microsoft uses both idioms in the various .NET related frameworks. The System.Windows.Forms namespace seems to rely heavily on nested implementations. I tend to find myself prefering this method as well even though it requires more work.

Recommendations, comments, alternative ideas?

+1  A: 

Personally I prefer the peer implementation, it promotes reuse of code which I don't think the nested implementation does. If another class needs to implement a different way of storing a collection of the same elements then another class can easily be implemented for that scenario without limiting code reuse.

A nested setup can also lead some developers to tightly couple their code to the parent class.

Michael Edwards
What happens when the shared collection uses an array as backing store, but the parent really needed them stored in a red-black tree keyed by property Foo? You could subclass the shared collection and add the new data structure, but then the object consumes twice the memory and removals all of sudden become O(n).
Brian Gideon
+1 In a very broad sense I like your statement about nested classes being tightly coupled to the parent class. However, sometimes I think there is merit in doing it that way. We strive to make our code as reusable as possible. But, sometimes we go so far that even though it can be used for a lot of different purposes it really does not do any one of those purposes optimally.
Brian Gideon
+2  A: 

Another option is to nest the collection class in the child class, and just name it Collection. That way, you always get Child.Collection as the name.

public class Child
{
  public class Collection : ICollection<Child>
  {
  }
}

public class Parent
{
  private Child.Collection children;
}
Tommy Carlier
different question, but a good idea...
Charles Bretana
Hmm...the implication here is that the child entity gets to dictate how everyone else gets to store collections of itself. That actually seems worse than the plain vanilla peer implementation.
Brian Gideon
@Brian, How So? Nesting the collection class changes only the namespace prefix necessary to uniquely identify the type, right ?
Charles Bretana
+5  A: 

Regardless of what Microsoft might have done in the past, the current .NET API design guidelines discourage creation of nested classes that are visible outside their parent classes. See http://msdn.microsoft.com/en-us/library/ms229027.aspx for details.

Nicole Calinoiu
There are two exceptions in the guideline both of which apply frequently for me.
Brian Gideon
No one really addressed what they do when different parents need to store children differently, but it is very hard to argue with the Framework Design Guidelines book (which I happen to have a copy of). So for now I'll accept this answer.
Brian Gideon
The people at Microsoft treat that book as more suggestions than rules. I'm not saying it's wrong, but that was my experience in Redmond.
tster
I'll thing you'll find life is much easier as a consumer of libraries if people treat that book as the Rule rather than just suggestions.
sixlettervariables
+1  A: 

I also prefer the peer approach. There's really no reason to nest the collection unless you will never use it outside of its parent class (in that case, it should be a private nested class.)

oltman
"in that case, it should be a private nested class" - not quite true, since it may be exposed on a public property of the class in which it is nested (but you still do not want any other class to be able to do the same, e.g. because implementation of collection is tightly coupled with its outer class).
Pavel Minaev
"unless you will *never* use it outside of its parent class" covers that, but this is definitely an important consideration.
oltman
So what do you do when you need to store the same type of object different ways? Do you create multiple collection class and call them say EntityCollectionForPurposeA and EntityCollectionForPurposeB without nesting?
Brian Gideon
I'm having trouble envisioning what these different ways would be. Maybe an example would help here? My first reaction to having that many collections to store the same type of object is that you're probably putting too much logic in the collection class. A more generic implementation and external functions handle what you need to do with the collection would probably be better, but again, I'm not sure what different ways you would need to store an object.
oltman
Lets say one parent needs to enumerate entities in order 50% of the time and the other 50% is random access with enough temporal proximity to justify a splay tree. Lets say another parent only ever needs to access them by index or enumerate in any order so an array would be better since it can be built in O(n) as opposed to O(n*log(n)) for the splay tree. We're talking about large numbers of entities so runtime complexity is critical.
Brian Gideon
By the way, I am actually okay with the EntityCollectionForPurposeA and EntityCollectionForPurposeB type of approach. I'm just playing devils advocate for the sake of seeing what others are doing.
Brian Gideon
Will these collections be read-only? It seems like any performance benefits you'd get by changing the way the collection is structured would be canceled out and then some by the overhead of keeping the collections synced.
oltman
+1  A: 

I would only use the nested arrangement when there is only one Entity in the Domain model that can logically contain the child Entities.

For example if you had a PieceOfMail class and a MailPieces collection class

  class PieceOfMail { } 
  class MailPieces: Collection<PieceOfMail> { }

then the ShipingCompany class, and the MailBox class, and the PostOffice Class, and the MailRoute class, and the MailManBag class, could ALL have a constituent property typed as MailPieces, so I'd use the "peer" technique.

But otoh, in the same Domain, if you had a class representing a type of PostageDiscount, and a collection class representing a set of discounts to be applied to a shipment, it might be the case that ONLY the ShipmentTransaction class could logically contain a collection of those discounts, then I'd use the nested technique...

Charles Bretana
Interesting perspective.
Brian Gideon
+1  A: 

Do you really need a ChildEntityCollection? Why not use a collection type that is provided?

  //why bother?
//public class ChildEntityCollection : ICollection<ChildEntity>{}

public class ParentEntity
{
   //choose one
  private ChildEntity[] children;
  private List<ChildEntity> childrenInList;
  private HashSet<ChildEntity> childrenInHashSet;
  private Dictionary<int, ChildEntity> childrenInDictionary;
   // or if you want to make your own, make it generic
  private Balloon<ChildEntity> childrenInBalloon;
}
public class ChildEntity
{
}
David B
Yeah, I'd say in most cases that's probably sufficient.
Brian Gideon
+1  A: 

I generally try to avoid generating specific collection classes. Sometimes you may need a special class, but in many cases you can simply use generic classes like Collection<T> and ReadOnlyCollection<T> from the System.Collection.ObjectModel namespace. This saves a lot of typing. All your collections derive from IEnumerable<T> etc. and are easily integrated with LINQ. Depending on your requirements you could also expose your collections as ICollection<T> or another collection interface and then let classes with specific requirements use highly optimized generic collections.

public class ParentEntity {

  Collection<ChildEntity> children = new Collection<ChildEntity>();

  public Collection<ChildEntity> Children {
    get {
      return this.children;
    }
  }

}

You can also wrap an IList<T> like this:

public class ParentEntity {

  // This collection can be modified inside the class.
  List<ChildEntity> children = new List<ChildEntity>();

  ReadOnlyCollection<ChildEntity> readonlyChildren;

  public ReadOnlyCollection<ChildEntity> Children {
    get {
      return this.readOnlyChildren
        ?? (this.readOnlyChildren =
              new ReadOnlyCollection<ChildEntity>(this.children));
    }
  }

}
Martin Liversage