OdeToCode IC Logo

Wrapping about Collections

Monday, February 1, 2010

Mark Needham’s “Thoughts On Software Development” is a great collection of blog posts. Last year Mark wrote “Coding: The primitive obsession”, and challenged the idea that the primitive obsession anti-pattern is just about overusing low level data types like string and int.

The most frequent offender seems to be the creation of collections of things which we then query to find specific items elsewhere in the code. More often that not we'll also perform some custom logic on that particular item.

At its most extreme we might extract each of the items in a collection and make use of them separately. This somewhat defeats the purpose of the collection.

When this happens it often seems to me that what we actually have is a series of attributes of an object rather than a collection.

Mark goes on to describe how wrapping a collection can often create a more useful abstraction, then points to his debate in Wrapping collections: Inheritance vs Composition. It’s a good read.

I’ve been wrapping collections a bit myself lately, and it’s not too hard to create a custom collection that is both feature rich and descriptive of the problem it solves using composition. For example, let’s say you need to keep a collection of Stamp objects and support INotifyCollectionChanged.

public class StampCollection : IEnumerable<Stamp>, 
                               INotifyCollectionChanged
{
    public StampCollection()
    {
        _items = new ObservableCollection<Stamp>();
        _items.CollectionChanged += ItemsCollectionChanged;
    }

    public void AddStamp(Stamp newStamp)
    {
        /* logic */

        _items.Add(newStamp);
    }

    public void RemoveStamp(Stamp stamp)
    {
        /* logic */

        _items.Remove(stamp);
    }

    public IEnumerator<Stamp> GetEnumerator()
    {
        return _items.OrderBy(stamp => stamp.Something)
                     .GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public event NotifyCollectionChangedEventHandler 
        CollectionChanged = delegate { };

    void ItemsCollectionChanged(object sender, 
                                NotifyCollectionChangedEventArgs e)
    {
        CollectionChanged(this, e);
    }

    readonly ObservableCollection<Stamp> _items;
}

Yes, it’s a bit of code, but it:

  • Gives you complete control over the surface area of the API compared to inheriting from ObservableCollection<T> or List<T>
  • Allows precise control over adding and removing objects (validation, perhaps updating fields).
  • Becomes a more descriptive part of the domain
  • Is not to be confused with a rap collection.