1

A different question of mine had to do with encapsulating member data structures inside classes. In order to understand this question better please read that question and look at the approach discussed.

One of the guys who answered that question said that the approach is good, but if I understood him correctly - he said that there should be a class existing just for the purpose of wrapping the collection, instead of an ordinary class offering a number of public methods just to access the member collection.

For example, instead of this:

class SomeClass{

    // downright exposing the concrete collection.        

    Things[] someCollection;

   // other stuff omitted

    Thing[] getCollection(){return someCollection;}

}

Or this:

class SomeClass{

    // encapsulating the collection, but inflating the class' public interface.

    Thing[] someCollection;

    // class functionality omitted.

    public Thing getThing(int index){
        return someCollection[index];
    }

    public int getSize(){
        return someCollection.length;
    }

    public void setThing(int index, Thing thing){
        someCollection[index] = thing;
    }

    public void removeThing(int index){
        someCollection[index] = null;
    }

}

We'll have this:

// encapsulating the collection - in a different class, dedicated to this.

class SomeClass{
    CollectionWrapper someCollection;

    CollectionWrapper  getCollection(){return someCollection;}
}

class CollectionWrapper{

    Thing[] someCollection;

    public Thing getThing(int index){
        return someCollection[index];
    }

    public int getSize(){
        return someCollection.length;
    }

    public void setThing(int index, Thing thing){
        someCollection[index] = thing;
    }

    public void removeThing(int index){
        someCollection[index] = null;
    }

}

This way, the inner data structure in SomeClass can change without affecting client code, and without forcing SomeClass to offer a lot of public methods just to access the inner collection. CollectionWrapper does this instead.

E.g. if the collection changes from an array to a List, the internal implementation of CollectionWrapper changes, but client code stays the same.

Also, the CollectionWrapper can hide certain things from the client code - from example, it can disallow mutation to the collection by not having the methods setThing and removeThing.

This approach to decoupling client code from the concrete data structure seems IMHO pretty good.

Is this approach common? What are it's downfalls? Is this used in practice?

  • 2
    I can't say I've ever really come across the need in developing any software to change a list into an array. The other way around, sure, but this has just led to my operating principle being "just use a list, you might need its features later". Don't know if other people feel the same way, though... – Jules May 28 '14 at 18:06
  • 1
    Why bloat your class with methods that regurgitate basic collection behavior? Why not provide a collection property that provides the type of access you would like to give to your collection? Ex: public IEnumerable<T> Items {get{};} //read only – CheckRaise May 28 '14 at 19:26
  • Assuming your example is non-trivial, it seems like simple composition to me. – Robert Harvey May 28 '14 at 19:48
  • 2
    Why aren't you just implementing Collection and then passing that around? Which would allow it to work with all of the existing collection utilities rather than forcing the client to peak into the abstraction to do standard things? – user40980 May 28 '14 at 23:00
  • 1
    @supercat agreed; performance is the only consideration that I can think of in favour of arrays... but it is unusual in my experience that this degree of performance is required. The overhead in processing 1,000 items from a list iterator versus an array is perhaps in the order of a million processor cycles, which is trivial for most applications. My experience is that the time taken by the actual processing to be performed on the items usually dwarfs this amount of time. And using collections, which can be easily wrapped, e.g. with an immutable wrapper, may avoid the requirement to copy. – Jules May 29 '14 at 5:37
6

You are implementing a restricted set of the Collection interface, without actually giving it something that you can work with.

E.g. if the collection changes from an array to a List, the internal implementation of CollectionWrapper changes, but client code stays the same.

This is exactly what the Collection interface is intended to do. If you pass back a Collection, that is all the client can work from.

Also, the CollectionWrapper can hide certain things from the client code - from example, it can disallow mutation to the collection by not having the methods setThing and removeThing.

In Java, this is known as an Unmodifiable Collection, and the Collections utility has just such a method to convert a modifiable collection to an unmodifiable one.

By making the restricted wrapper you are also giving up things such as sort and iterator which come in very handy from time to time. This means code such as:

for(Thing t : someCollection) {
    ....
}

won't work. Instead, the code would need to be:

for(int i = 0; i < someCollection.size(); i++) {
    Thing t = someCollection.get(i);
    ....
}

While that is perfectly acceptable code, it also means you are failing to take advantage of the language to allow the client to write code faster (programmer time is expensive).

The way you decouple the implementation is to use an interface, preferably one that already exists in the language framework so that other things that use that same interface can use your code without jumping through additional hoops.


Just a note:

What I'm trying to do, is not expose the collection - because I don't want the client code to break when the collection changes say from a List to a Set. If I just allow the client code to do class.getCollection() it'll break when the type of the collection changes. So the idea is to make a thin layer around it ("CollectionWrapper"), so when the type of collection changes, only the wrapper has to change, but not the code using the collection (i.e. using the wrapper). Is what I'm saying clear?

If the collection is only given as a Collection interface, it won't break. However, I must point out that the public methods that you are presenting with this class, namely public Thing getThing(int index), public void setThing(int index, Thing thing), and public void removeThing(int index) would preclude the implementation from changing from a List to a Set, because Sets have no concept of indexes and these methods would need to change.

  • I will sometimes return an immutable collection, seems to be the quickest way to implement the idea of "iterate over objects in some other object." I also like the idea of the "collection wrapper" being the collection interface itself: it is already written and tons of code in the real world already uses it. – user22815 May 30 '14 at 16:28
1

You're describing the Encapsulated Collection pattern. I like this pattern a lot; it's especially appropriate to domain-driven design.

The advantage you describe - being able to change the internal representation of the collection - is a good one. But I think the bigger advantage is restricting clients' ability to modify or break the underlying collection.

For example, consider a "commenting" feature of a social network, with the requirement that you can't delete comments after they've been posted. If you expose the List<Comment> as a property, it's easy for clients of your class to call the list's Remove method without realising they weren't supposed to.

If, instead, you use a Comments class with a private List<Comment> and no Remove method, it's now impossible to break the invariant.

You also end up with a richer domain model, which is better equipped to deal with new features. (A collection of domain objects is often a new domain concept that your model is missing.) Imagine that the requirement's changed and now it's possible to delete comments, but the deletion has to be recorded in an audit trail. It's easy to implement this behaviour in a new Delete method on your Comments class - it would have been much more complicated to override the pre-existing Remove method on the List.

0

I think you are right.

I have developed several custom collections in diferent programming languages, and, sometimes, I applied the pattern. I also check that other developers also applied.

It seems that it depends on what the developer wants to achieve, sometimes, the collection is access directly, sometimes, using the pattern.

Visual controls with several items like "Treeview", "Listview", "Gridview" use a lot of this technique.

0

You said,

What I'm trying to do, is not expose the collection - because I don't want the client code to break when the collection changes say from a List to a Set.

I have concerns about that requirement -- whether perceived or real.

The abstractions represented by those standard containers are so distinct and different, I find it hard that you will be able to substitute one for another in your interface. If SomeClass provides an interface that is more appropriate for a Set, you have to honor that interface regardless of how you implement the Set. The same logic applies for List too. In that sense, you don't have to expose ContainerWrapper or Container at all. They are implementation details of SomeClass. You could use pimpl pattern to completely hide the container.

Perhaps SomeClass is a container of objects with additional meaning.

For example, in a CAD program, an Assembly consists of sub-components that can be Parts or sub-Assemblies. However, that is not sufficient to capture the relationship between an Assembly and one of its sub-components. The location and orientation of the sub-component in the Assembly is the key piece of data that makes this particular container-contained relationship work. For this case, whether you store the collection of Pair<sub-component, transform> as a List or a Set is of very little importance in the interface of Assembly. You need to be able to query the Assembly for its sub-components and given a sub-component, you need to be able to query the Assembly for the transform of the sub-component in the Assembly.

Another example: A Canvas and its contained objects in a 2D painting program or an SVG program. Here again, the container-contained relationship has to be created using the 2D position of the contained objects plus a depth to in order to arrange them in layers. Here also, you can use a Set or a List to hold the contained objects.

In conclusion, I am asking you to dig deeper to determine the kind of relationship SomeClass has with its contained objects. Is it a matter of simple containment or is there more to the relationship? If there is more to the relationship, how would you expose the relationship that is divorced from a List or a Set.

In both the examples I presented, a List makes more sense to me. I can't say what makes more sense for SomeClass.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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