Take the 2-minute tour ×

I'm working on a small tool as a toy project to show the difference between two directories, showing which files/directories were added, removed, modified, etc.

I was trying to represent these changes as simply 'ChangeItem' objects, without distinction between whether it was a file or directory. However, that created a lot of problems, e.g how to display them in a tree, how to know who the parent of a child is, etc. And it was also very unintuitive.

I then split the changes between directory changes and file changes. That immediately made it very easy to code and to understand what was going on. Now its a lot simpler to select all files in a directory, etc.

My question is, how can one know whether to use abstraction or to get more specific in their code? How can you tell if you have too much or too little abstraction?

share|improve this question
add comment

6 Answers

up vote 5 down vote accepted

How does a painter know whether it is too much or too little of purple?

He mixes the colors, tries a stroke, make a few sketches and sees what happens. Then he adjusts the proportions until the entire drawing looks nice and radiates harmony.

We do the same with code. We get a first-time implementation idea, reflect on it, analyze its strong and week sides then try it to see if it works. Then we tune the idea in an iterative process to adjust the proportions of abstractivness, encapsulation, polymorphism and whatever until it looks right and work as needed.

share|improve this answer
add comment

You write concrete code first.

You write abstract code when you must because it simplifies concrete code.

It easiest to start with concrete and find the abstractions after studying what's similar and different about the concrete.

share|improve this answer
Always remember the Rule Of Threes: an abstraction without at least three clients isn't an abstraction. It's wishful thinking. Usually wrong. Good abstraction are extracted, not designed. –  Jörg W Mittag Apr 26 '11 at 3:24
add comment

You write abstract code when you are writing libraries, and must generalize the functionality so that it will work under a wide variety of conditions.

But writing libraries this way is hard. In a normal application (say, a line of business application), this kind of generalization is considered a form of "premature optimization," generally characterized as "You aren't going to need it" (YAGNI).

There is a tipping point where repetitive code demands that a more generalized solution be designed. But typically this kind of refactoring to remove redundancy is much simpler than writing a generalized library.

In the end, the additional complexity required to implement abstract solutions must be justified by the flexibility they offer.

share|improve this answer
add comment

By reading the question I would say that you can be both abstract and specific. Is just a matter of context, use the most abstract representation possible in each context.

For your specific toy app, ChangeItem is the most abstract representation of the changes. Then you get more specific with DirectoryChangeItem and FileChangeItem, through inheritance. The you can use a composite pattern to model the tree. When you want to display it you can use the specific representations and when traversing it you can use the abstract representation.

And to give a concrete answer to the question: be as specific as possible until you feel that you need another layer below that.

share|improve this answer
add comment

The rule of thumb that I follow, which is fairly common, is that you should not try to abstract something until you find yourself writing it for the third time.

The first time you simply don't understand the problem domain and will wind up over-architecting all the wrong pieces. The second time you are perfectly positioned to build the ideal solution for your last problem. The third time you are finally in a good position to come up with appropriate abstractions that will help you on things that need to change, and not get in your way on getting the simple stuff done.

share|improve this answer
3rd time is a good rule of thumb. I find it rare to generalize very much before the 3rd time writing something. –  qes Apr 25 '11 at 22:34
Doesn't it also depend on how large the similar code is? You wouldn't copy and paste two pages of code that were only different by two lines, just because you hadn't hit the magical "3 times" yet. –  Scott Whitlock Apr 25 '11 at 23:25
@scott-whitlock: Obviously. That is why it is a rule of thumb. Ignore it as appropriate. Furthermore there is a difference between organic abstractions (eg "I needed bar to be almost exactly foo, so I just added an optional parameter to foo") and abstractions that are part of your up front design. –  btilly Apr 25 '11 at 23:58
@Scott Whitlock: "You wouldn't copy and paste two pages of code that were only different by two lines". That's not a call for abstraction. That's still just good design. The 3x rule has to keep very, very strictly to avoid prematurely abstract designs. –  S.Lott Apr 26 '11 at 0:01
"As appropriate" is the key phrase here. My two cents - often the "prefer composition to inheritance" applies. Sometimes a problem some solve with an abstraction would be better as if (cond) { //use one object} else {// use the other object }, esp. when it's a binary decision set. –  Michael K Apr 26 '11 at 14:55
add comment

Typically abstraction is useful to combat things like Complexity, Entropy, to make your code Cybernetic, and even at a low level readability. I would hardcode first - ONLY if abstraction is not obvious up front. Most abstraction occurs when more than one implementation share the same pattern.

Think of abstraction to be the congruence or unity of 2 or more Sets. Take a Venn Diagram as a simplistic model: The part where the circles overlap, or in other words, the Sets merge.

If I have two sets: A:{a,b,c,d,e} and B:{d,e,f,g,h}. The point where I would start looking to abstract is the unity of A + B; {d,e} is where to abstract. Similarly, if those in the difference of A & B (A-B or B-A) are isomorphic to each other, meaning its low cost to create a,b or c from f,g or h (and vise versa), then I would keep abstraction in the back of my mind.

share|improve this answer
add comment

Your Answer


