Cute Little Abstractions

by Jeff Langr

June 17, 2009

I’m refactoring several classes of over 2000 lines each. These are classic god-classes that mediate a handful of stubby little data-centric classes around them. I reduced the first, primary domain class by over 1100 lines, to under 1500, by simply pushing responsibilities around. Adding tests after the fact (using WELC techniques, of course) gave me the confidence to refactor. In just a few hours, I’ve been able to completely eliminate about 700 lines so far out of the 1100 that were shifted elsewhere. I’m loving it!

After cleaning up some really ugly code, I ended up with better (not great) code:

    private boolean containsThreeOptionsSameType() {
       Map types = new HashMap();
       for (Option option : options)
          increment(types, option.getType());
       return count(types, Option.ALPHA) >= 3 || 
              count(types, Option.BETA) >= 3 || 
              count(types, Option.GAMMA) >= 3 || 
              count(types, Option.DELTA) >= 3;
    }
    
    private int count(Map map, String key) {
       if (!map.containsKey(key))
          return 0;
       return map.get(key);
    }
    
    private void increment(Map map, String key) {
       if (!map.containsKey(key))
          map.put(key, 1);
       else
          map.put(key, map.get(key) + 1);
    }

I could clean up the complex conditional on the return statement (perhaps calling a method to loop through all types). Also, the count and increment methods contain a bit of duplication.

More importantly, the count and increment methods represent a missed abstraction. This is very common: We tend to leave little, impertinent, SRP-snubbing, OCP-violating methods lying about, cluttering up our classes.

(Aside: I’m often challenged during reviews about exposing things so I can test them. Long-term, entrenched developers are usually very good about blindly following the simple rule about making things as private as possible. But they’re perfectly willing to throw OO concept #1, cohesion, completely out the window.)

I decided to test-drive the soon-to-be-no-longer-missing abstraction, CountingSet. I ended up with an improved implementation of increment by eliminating the redundancy I spotted above:

    import java.util.*;
    
    public class CountingSet {
       private Map map = new HashMap();
    
       public int count(T key) {
          if (!map.containsKey(key))
             return 0;
          return map.get(key);
       }
    
       public void add(T key) {
          map.put(key, count(key) + 1);
       }
    }

Some developers would be appalled: “You built a cruddy little class with just four lines of real code? And for only one client?” And further: “It’s only a subset of a counting set, where are all the other methods?”

Sure thing, buddy. Here’s my client:

    private boolean containsThreeOptionsSameType() {
       CountingSet types = new CountingSet();
       for (Option option : options)
          types.add(option.getType());
       return types.count(Option.ALPHA) >= 3 || 
              types.count(Option.BETA) >= 3 || 
              types.count(Option.GAMMA) >= 3 || 
              types.count(Option.DELTA) >= 3;
    }

My client now only contains intent. The small ugliness with the parameterized map and boxing is now “information hidden.” I also have a simple reusable construct–I know I’ve had a similar need several times before. As for building and/or using a fully-functional CountingSet, there’s something to be said for creating “adapter” classes with the smallest possible, and most meaningful, interface.

Good enough for now. Don’t hesitate to create your own cute little abstractions!

Comments

Mike Bria July 1, 2009 at 10:36am

I dig this Jeff. “Some developers would be appalled” – f’ em. 🙂

Seriously though, I went through a similar flow a couple months ago that I posted about here if you’re interested.

Cheers,
MB


Jeff Langr July 1, 2009 at 8:08pm

Thanks for the link Mike–nice derivation. Also, many thanks for the InfoQ writeup!

Looking at the above code, I can’t help but continue to shake my head at the loss of expressiveness from not being able to use closures in Java yet. (That’s why I’m back on Ruby this month…)


chaussures nike air max June 15, 2011 at 4:01 am

That’s why I’m back on Ruby this month…


Jacob Zimmerman March 25, 2015 at 10:09 am

This goes along with the idea that private “helper” methods are often a code smell that says you need to delegate that responsibility to a different class.


Share your comment

Jeff Langr

About the Author

Jeff Langr has been building software for 40 years and writing about it heavily for 20. You can find out more about Jeff, learn from the many helpful articles and books he's written, or read one of his 1000+ combined blog (including Agile in a Flash) and public posts.