Sponsored Link •
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
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
leq methods were really misplaced. If I had a
Span class, I could place
leq there along with
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 (
find_span) and one "private" method (
class SpanFinder: def __init__(self,range_list): def find_span(self,index): def fold(self):The
Spanclass would have about six "public" methods and one "private" method (
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?
|Michael has been active in the XP community for the past five years, balancing his time between working with, training, and coaching various teams around the world. Prior to joining Object Mentor, Michael designed a proprietary programming language and wrote a compiler for it, he also designed a large multi-platform class library and a framework for instrumentation control. When he isn't engaged with a team, he spends most of this time investigating ways of altering design over time in codebases.|