My New Favorite Extreme OO Rule

Two code dojos ago, we tried using three Object Calisthenics rules (aka “Extreme OO” rules) in our kata exercise. The “Object Calisthenics” essay was published in The Thoughtworks Anthology. Since then, I have checked out the anthology from the YD library. After reading the original essay, I decided to apply these rules to the refactoring that I was doing.

The rule that stuck with me the most this time around was:

8. Use first-class collections. In other words, any class that contains a collection should contain no other member variables. The idea is an extension of primitive obsession. If you need a class that’s a subsumes the collection, then write it that way.

I had a class that I was refactoring (mostly for clarity) that among other things held a cache of data. For simplicity sake, let’s say the cache was held in a Map<Foo, Bar>. The class then went on to get, put, iterate, and remove from that map.

So, I applied Rule #8 and created a FooBarLookup class that encapsulated the Map<Foo, Bar>. I then had the freedom to only add the methods to it that I needed like Bar get(foo) and void add(Foo, Bar).

The most important thing that I realized is that I could abstract more powerful methods. For example, when I was done with a given Bar, I removed it from the FooBarLookup. In this Map, I had many Foo‘s mapping to the same Bar. So, previously, I had wrote code that iterated through the Map.entrySet() and removed entries that had matching Bar‘s. Now, I could now hide this iteration in a new method on FooBarLookup: boolean remove(Bar).

Big whoop, you say.

Well, one nice thing about this encapsulation is if I decided later that I needed to speed up this new remove(Bar) method, I could maybe add a Multimap reverse lookup to feed the keys to remove into the main Map. Then, I can manage the bidirectional map setup with the same add(Foo, Bar) method that I had defined before.

This new FooBarLookup class also gave me the ability to write some high-order-function-esque methods that map or filter the Foo‘s and Bar‘s in the Lookup. Happily, all of this iteration noise is hidden inside the encapsulated collection, and away from my business logic.