Not that every line is extracted into its own method, but most concepts are. Rule of thumb: If it has a comment above it, extract it into its own method and turn the comment into a good method name. The result is ShortMethods.
In some cases, you end up with a very large number of conceptually simple methods. This is a GoodThing.
LotsOfShortMethods can provoke the same fears as in FearOfAddingClasses.
LotsOfShortMethods invariably leads to the addition of one or more new [little] classes.
These two statements seem to be contradictory.
Someone with FearOfAddingClasses will probably not be the type of programmer to use LotsOfShortMethods in the first place, so it all works out beautifully. :)
I offer myself as a counterexample. I tend to write LotsOfShortMethods or refactor existing code into them; I am much more timid when it comes to creating necessary classes.
Is this because you're working in a language which has a large coding overhead for extracting a class? When I program in CeePlusPlus, I am very reluctant to extract a class, because it usually means:
After creating a lot of little methods, you usually find one or more sets with common parameters lists that belong together in a class. Extracting functions into a class cleans up a class in the same way Extracting functions from a long function cleans up the function.
I've found that if you're only creating methods to simplify code (not because they are part of the interface, or you have multiple callers), the best heuristic is to only create methods when the code becomes confusing. This changes from developer to developer, naturally, so the density of methods changes from developer to developer. Nonetheless, it's more confusing to have seven layers of method calls to track through to find out what's going on than it is to inline everything. But it's more confusing to inline everything rather than push unnecessary distractions to another level of abstraction.
Really, much like writing, when you are writing a method, clarity comes by saying only what you have to say, yet saying everything you have to say. When doing transaction management, I'm interested in transaction management, not how to manage a vector-based data structure. That's why we have different levels of abstraction, after all. Jumping between them sporadically just makes for confusion. -- SunirShah
Sunir, are you speaking from your experience as a student, which emphasizes writing code over maintaining code? I assume you like longer methods than us code maintainers.
If you have to track through multiple layers of method calls to figure out what's going on, then the top level abstraction wasn't named very clearly. Naturally, there is a range of abstractability in some concepts. For example:
visit_Someone() { drive_to_their_house(); ring_doorbell(); go_inside(); }is conceptually cleaner than leaving in all the code that relates to how the car works, or the wiring of their doorbell, etc. However, naming is crucial, so you avoid looking at code such as the following:
visit_Someone() { drive(); ring(); go(); }where it might be necessary to look inside the method calls to figure out what they do. If they have MeaningfulNames, it shouldn't be necessary. -- JacobCohen
Unfortunately, "MeaningfulNames" suffers from some relativity problems. The naming is normally done by somebody who is too familiar with the context to easily identify with those (including himself) who will be reading the code later without the context.
Also as most examples do, it uses a very well known and understood domain. You could use almost any names and people would get it. It's much harder when you are in layers of abstract and artificial domains that few people understand.
I am not sure how expecting code developers to understand the problem domain is a problem. I would not expect a developer to come in from Mars, be given a section of code, and have him immediately understand what the needed steps are. I would expect, however, given a developer who is familiar with the problem domain and its terminology, that he should be able to find a problem area with just a few file searches. If it becomes more involved than that (to identify the code section for work), then the developer just needs to refactor the names to make them more meaningful. The names do not ned to be perfect when they are first written; they can get better over time.
I don't agree with the heuristic that a programmer only extract a method in when s/he is in danger of getting confused. I much prefer fine-grained functional decomposition. This may be a matter of preference and I may end up with exactly the same decomposition as Sunir does. Fine-grained decomposition hinges on good abstraction and naming. If it works out, the use of a method can be understood in the immediate context. If it doesn't work out it's worse than overly long methods, because other programmers will have to drill through the imperfectly sealed layers of abstraction until they find firm ground. -- MichaelSchuerig
Just a small correction. It's not in danger of getting confused, but actually confused. Otherwise YouArentGonnaNeedIt. -- ss
Sunir's "actually confused" is roughly the same as the original's "has a comment", because we add comments when we are confused.
It's been my experience that breaking code into reusable functions/methods is GoodThing, but that doing so makes it harder to see how the whole thing really works. So I usually resort to drawing diagrams to give a "high level view" of the calling connections of all those little methods.
Certainly, breaking code into LotsOfShortMethods enables you to make the top level "manager" functions more abstract and easier to understand in themselves: Their code suddenly becomes a very clear statement of (1) do X, (2) do Y, (3) for each P in Q do A and then B, (4) finish with Z. But none of this means much when the program isn't working quite right and you aren't really sure about the details of your Ps and Qs. -- JeffGrigg
I might add that this style has only come to me recently when I started to use UnitTests heavily. If my "Ps and Qs" have their own unit tests, which is usually the reason I split them into Ps and Qs in the first place, then finding out why the program isn't working is actually much easier in my experience.
The two rules that I use for breaking up methods are OnceAndOnlyOnce, which extracts duplicate code, and SeparateTheWhatFromTheHow, which says methods should consist of either multiple "whats" or one single "how" (methods should consist of multiple delegations or one single action). As far as I can tell, SeparateTheWhatFromTheHow is the same rule as KentBeck's ComposedMethod. -- StanSilver
Beck's SmalltalkBestPracticePatterns book has an excellent discussion about the pros and cons of LotsOfShortMethods, which leads up to the description of the ComposedMethod pattern.
Wouldn't this result is more code? You have more code that is devoted to interfacing. For example, if all methods were one line, then the app would be about 50% interfacing code (parameter declarations, names, etc.) and 50% actual code that does something.
Then again, a good compiler should be able to inline such calls with little difficulty...
I have usually found that this practice actually results in less code. One of the biggest reductions is in dead code segments that take up residence in large functions. A second reduction is due to exposing common code segments that can be shared rather than copied between large functions. A third reductions is due to improved code; it is amazing what is obvious when you are looking at a function of 20 lines, rather than the same 20 lines buried and spread across a multi-page function.
The first two: cleaning dead code and repetition factoring, are generally not the issues of contention here. I would like to see an example that illustrates your 3rd reason.
It seems that there a balance being sought here:
On the one hand, LotsOfShortMethods has several benefits such as improving readability (via RaiseAbstraction), and encouraging reuse (via HappyCollision). However, this is not automatic, as it relies heavily on careful use of MeaningfulNames, which in turn depend on conceptualizing AppropriateAbstractions?.
On the other hand, inappropriate use of LotsOfShortMethods can lead to CodeFragmentation - where the call-graph becomes unnecessarily deep and wide - thus degrading comprehensibility, and maintainability. -- NaimRu
CodeComplete gives empirical evidence that LotsOfShortMethods (fewer than 20 lines, as I recall) decreases maintainability.
See ShortMethods, CommentTheWhy, SeparateTheWhatFromTheHow, RavioliCode, LongFunctions, ManyShortMethodsPerClass