The Artima Developer Community
Sponsored Link

Let's Reconsider That
Iceberg Classes
by Michael Feathers
August 30, 2005
About 88% of an iceberg's mass is below water. How much of your class is down there too?


There are many ways to end up with odd designs and everyone has a different notion of oddness, but one thing that always strikes me as odd are classes that have fewer public methods than private methods. Classes that look like that remind me of icebergs. They have one or two public methods above water and a lot of mass below, lurking in the private methods. To me, it often means that there's some other abstraction hidden away inside the class that might be useful to pull out.

The thing that is funny about iceberg classes is that they superficially look like good design. After all, the argument goes, encapsulation is a hallmark of OO, so if we have more encapsulation we have better design. Two public methods and seven private methods? That must be good, we've encapsulated more.. but is it really?

Here's a synopsis of a Python iceberg class:

class SpanFinder:
	def __init__(self,range_list):	
	def find_span(self,index):	

	def fold(self):
	def geq(self,left,right):
	def leq(self,left,right):
	def first_of(self,index_string):
	def second_of(self,index_string):
	def segment(self,index_string,which):
	def in_span(self,index,span):	

SpanFinder is part of a little Python IDE I was working on a while ago. You give it a list of character offsets which represent the starting and ending positions of method names in a program listing, and then you can use its find_span method to get back an pair offsets (a span) that enclose a position you pass to it. The only methods here that are "public" are __init__ and find_span. All of the other methods are called from find_span (directly or indirectly). It's a typical iceberg, but does it have to be an iceberg at all?

Well, one of the things that I noticed when I was writing it was that the geq and leq methods were really misplaced. If I had a Span class, I could place geq and leq there along with first_of, second_of, and in_span. The segment method is used internally in those methods and it could come along for the ride. With that move, I could cut the iceberg down to size.. two "public" methods (__init__ and find_span) and one "private" method (fold).

class SpanFinder:
	def __init__(self,range_list):	
	def find_span(self,index):	

	def fold(self):
The Span class would have about six "public" methods and one "private" method (segment):

class Span:
        def __init__(self,span_representation):
	def contains_index(self,index):    # was in_span	
	def geq(self,right):
	def leq(self,right):
	def first(self):
	def second(self):

	def segment(self,index_string,which):

Would it be worth it? At the the time I didn't think so (that's why I was able to find the code in this state) but, I remember seeing the issue and thinking that if I ever went back and had to change that class, the refactoring might be worthwhile.

The thing that I'm pretty curious about, though, is whether it is always this way.. whether every iceberg class has a decent, applicable extract class refactoring. I tend to feel this way about long methods, that any method that has more than say ten lines really is doing more than one thing (unless it is just some long initializing method) and that there is some decent method extraction that you can find if you hunt for it.

I have a feeling that there is something to this, that for any class that has, say, more than 60-70% private methods and more than, say, four or five methods, there's always a decent class extraction that can be done. Anyone have any counter-examples?

Talk Back!


RSS Feed


About the Blogger



Sponsored Links

