Where do you put the classes?
I'm not sure if there is already a nomenclature for this, but for this question, let's define two terms: peer implementation or nested implementation to illustrate how you would implement collection classes in a data model that contains many parent / child relationships.
I use the term peer to describe a scenario in which you implement collection classes at your model level, along with entity classes that basically make them peer-to-peer in your API:
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 store child objects of the same type.
I use the term nested to describe a scenario where you implement them as a nested class:
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 their children in a way that is most optimized for that particular parent. For example, one parent might find that the array data structure works well, while the other might use a play tree (unknown what I know, but it illustrates my point well).
I've noticed that Microsoft uses both idioms in various .NET-based frameworks. The System.Windows.Forms namespace seems to be heavily dependent on nested implementations. I also prefer this method, even if it requires more work.
Recommendations, comments, alternative ideas?
source to share
Regardless of what Microsoft may have done in the past, current .NET API design guidelines prevent the creation of nested classes that are visible outside of their parent classes. See http://msdn.microsoft.com/en-us/library/ms229027.aspx for details .
source to share
Personally, I prefer peer implementation, it promotes code reuse, which I don't consider to be a nested implementation. If another class needs to implement a different way of storing a collection of the same elements, then another class can be easily implemented for this scenario without restricting code reuse.
Nested customization can also lead some developers to tightly tie their code to the parent class.
source to share
I would only use nested layout when there is only one entity in the domain model that can logically contain child entities.
For example, if you have PieceOfMail class and MailPieces collection class
class PieceOfMail { }
class MailPieces: Collection<PieceOfMail> { }
then the ShipingCompany class and MailBox class, and the PostOffice class and MailRoute class and MailManBag class could ALL have a composite property printed as MailPieces, so I would use the "peer" method.
But otoh, in the same Domain, if you have a class representing the PostageDiscount type and a collection class representing the set of discounts that will be applied to the shipment, maybe the ONLY ShipmentTransaction boolean operation class contains a collection of these discounts, then I would use a nested method...
source to share
Do you really need a ChildEntityCollection? Why not use the 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
{
}
source to share
I usually try to avoid generating specific collection classes. Sometimes you may need a custom class, but in many cases you can just use generic classes such as Collection<T>
and ReadOnlyCollection<T>
from namespace System.Collection.ObjectModel
. This saves a lot of typing. All your collections are sourced from IEnumerable<T>
etc. And easily integrate with LINQ. Depending on your requirements, you can also expose your collections as ICollection<T>
or a different collection interface, and then allow classes with specific requirements to 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 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));
}
}
}
source to share