Wrap Collections

Create classes for your collections instead of using native collections directly.

As we already covered in Inherit Wisely, a collection of objects isn't just an array of objects.

A collection is a concept.

An array is a storage unit.

For example, a collection can be represented as an array of contiguous entries in memory, or as a linked-list. As far as we're concerned, it's still a collection.

It has a simple interface that screams "list of things".

interface Collection<T> {
    get(index: number): T;
    insert(value: T): void;
    remove(value: T): T;
    removeAt(index: number): T;
    clear(): void;
}

Now again, what's an array ? What's a linked list ?

It's an implementation detail.

So we would well enough have two implementations of a Collection.

class ArrayCollection<T> {
    // implementation using arrays
}

class LinkedListCollection<T> {
    // implementation using linked lists
}

Now, which one should we use, say, for a collection of users ?

Well, when we're modeling the interactions between the objects, we don't care at all. It's secondary.

It's only once we begin to develop an actual collection that we can start thinking about it.

But it's important to keep our power and to be able to change implementation without any consequences.

That's one again the power of abstraction. As pragmatic object thinkers, we want to deal with high-level abstract concepts to keep a 100 feet view of our use case. That our collection is implemented using an array or a linked list is a terribly low level of detail, one that increase our cognitive load unnecessarily.

Will you ever talk to your users about arrays and linked lists ? If they know about it, you fucked up.

What's more, having a collection allows you to manipulate a list of objects seamlessly.

What if you want to get items matching a specific filter, or delete users with a specific property ? There's no place like a custom collection.

class UsersCollection extends Collection<User> {
    legalUsers() {
        // implementation
    }
    
    deleteUnderages() {
        // implementation
        // note : please do not delete underage people, they have the right to live
    }
}

Putting this logic anywhere else would simply lead to procedural code.

That's leaking logic out of the responsible object.

And objects don't like having their responsibilities taken away.

Last updated