Type safety is a very minor reason to use first-class collections. From your link:
Rule 4: First class collections
Application of this rule is simple: any class that contains a collection should contain no other member variables. Each collection gets wrapped in its own class, so now behaviors related to the collection have a home. You may find that filters become a part of this new class. Also, your new class can handle activities like joining two groups together or applying a rule to each element of the group.
The idea here is if you find yourself searching, filtering, validating, or anything beyond add/remove/iterate semantics on a collection, the code is asking you to put it in its own class. If you need to update just one value (after a search), that probably goes in the collection class.
The reasoning for this is pretty simple, collections tend to get passed around. Soon enough, 4 different classes have their own SearchByID()
method. Or you get return values like Map<Integer, String>
with the context of what's stored in that map stripped away. A first-class collection is a simple solution that costs a single source file. In practice, once those are in place (they're very easy to write unit tests for as well), any change dealing with the collection is easy to handle, like when SearchByID
needs to take a GUID instead of an int.
Lag/Latency? I call BS on that. There should be exactly zero overhead from this practice. (Edit: It has been pointed out in the comments that this can, in fact, inhibit optimizations performed by the HotSpot VM. I don't know enough about VM implementation to confirm or deny this. I was basing my comment off of the C++ implementation of virtual functions.)
There is some code overhead. You have to create all the constructors from the base class that you want, forwarding their parameters.
I also don't see it as an anti-pattern, per se. However, I do see it as a missed opportunity. Instead of creating a class that derives the base class just for the sake of renaming, how about you instead create a class that contains the collection and offers a case-specific, improved interface? Should your widget cache really offer the full interface of a map? Or should it instead offer a specialized interface?
Furthermore, in the case of collections, the pattern simply doesn't work together with the general rule of using interfaces, not implementations - that is, in plain collection code, you would create a HashMap<String, Widget>
, and then assign it to a variable of type Map<String, Widget>
. Your WidgetCache
cannot extend Map<String, Widget>
, because that's an interface. It cannot be an interface that extends the base interface, because HashMap<String, Widget>
doesn't implement that interface, and neither does any other standard collection. And while you can make it a class that extends HashMap<String, Widget>
, you then have to declare the variables as WidgetCache
or Map<String, Widget>
, and the first loses you the flexibility to substitute a different collection (maybe some ORM's lazy loading collection), while the second kind of defeats the point of having the class.
Some of these counterpoints also apply to my proposed specialized class.
These are all points to consider. It may or may not be the right choice. In either case, your colleague's offered arguments are not valid. If he thinks it's an anti-pattern, he should name it.
Best Answer
Probably
There are different use cases as well as different schools of thought in programming.
However, from my experience, I never regretted putting a collection of things into its own class. It allows you to restrict access in the way the collection is meant to be used, plus you can name those "access points" (= methods, properties etc).
Why not only LINQ
Sure, with LINQ you can easily filter, sort, remove all elements that have condition x etc, but if you create a class containing the collection (
private
-ly), you can restrict the class to only do that a certain way. For example, you can provide a standard way of filtering. This is basically the OOP concept of encapsulation.By the way, I'm not advocating against LINQ, because it's amazing. The LINQ queries just tend to end up in a method on my "collection classes' and look a lot like what Martin Fowler advises in this regard. It's a long article, but the first example is enough to demonstrate what I mean:
Advice: Encapsulate collections
Essentially, an own class
ArgumentList
can provide additional functionality, while it doesn't take anything away. Whatever you intend to use, you can expose - whatever you don't intend to be used can't even be used.This also means you now only have to care about
ArgumentList
's interface and not about its implementation. I had my fair share of cases where such a "collection class" made it obvious that I didn't actually need aDictionary<>
and aList<>
was enough. Furthermore, it was then also easy to change it to aList<>
because I only needed to change the collection class itself - just replace the collection and adjust the code in the methods so they still do what they say.Now as a summary of all that,
IDictionary<string, ArgumentList>
looks better than your given example, but in a way that's only a symptom of something bigger.PS
That being said, I do have a part in my current code base that uses collections similar to your example. Because it's "just shifting some data around until it fits". However, every other place where I used a "collection class", the code is way easier to understand and I'm considering changing this part, too. You know, when I have the time :D
Also, in case you use IoC containers, "collection classes" are more injection friendly than bare-bones collections.
I'd advise you to read the question and accepted answer here. It's not a duplicate or even a similar question, but the accepted answer has quite some overlap in information. It's a very nice explanation of how collections are meant to be used in C#, by one of C#'s creators no less - it definitely was a bit of an aha moment for me.