When implementing a function, I clearly know what the function will do and how. I know which services will be used within or without class scope; I limit the function’s responsibility and anticipate a concise implementation.
Over time, requirements will grow; more true for generic ‘milestones’ (setup, update functions, etc…). Sprouting functions avoid growing large functions, however this requires defining new semantics; naming functions is time-consuming and does not contribute to solving the problem at hand. When would we ignore the sprouting rule and let functions grow? Is there a workable compromise?
I usually break down function bodies into paragraphs, typically annotated as follows:
// do this...
...
// do that
...
…and so forth; breaking down a function into commented paragraphs is the first remedy I apply when spotting hard to read, bulleted functions; this takes less time than sprouting functions and may be a good preliminary to the latter; however, I do not yet plan on sprouting functions later on:
- Commented paragraphs may be clearer than uncommented sequences of function invocations.
- Commented paragraphs immediately reveal behaviour implementation.
- Considering flow, this is less intrusive than defining a new function – no need to investigate concise, clear word concatenations; no need to layout an auxiliary function ‘somewhere else’ in a class file and scroll back and forth (in my experience, sprouting functions within a plain text editor will corrupt any logical ordering you attempt imposing on your members)
A good developer won’t advise you doing this, however:
- Sprouting a function instead would scope your code – you will easily know what variables are involved in carrying a substep (if you’re evil, you will define class level privates to avoid passing arguments, and lose this benefit)
- You are violating the single responsibility principle.
- You are limiting opportunities to reuse the code.
- Granted correct naming, sequences of slave function invocations are even more readable than commented paragraphs: sub-level statements hinder not our understanding of the main function.
- Comments are unreliable – comments don’t compile and get out of date once commented code has been updated.
You may still feel uncomfortable with sprouting functions – so do I, and here’s why:
- Sprouting functions obscure internal class design. If we afford inner classes and time enough to keep implementations tidy, I have no qualms. But do we?
- A private function used only once within a class formally advertises itself as reusable. If you are collaborating, somebody might start using a private member not actually meant for reuse. Another developer will modify the slave function for the benefit of either caller, potentially breaking your code.
- Designing readable, concise function names is hard enough. A comment in the context of a sequence of instructions isn’t. In order to correctly name a sprouting a function, you may need to express the function’s intended invocation context as well as its intended behaviour.
- Over time, sprouted function names are no more reliable than commented code paragraphs. Beyond self-consistency, function names are no less arbitrary to compilers than comments; meanwhile they are harder to change.
As a rule, I don’t sprout a function unless I need to share the code, perceive clear responsibility and safely anticipate accidental reuse.
=> In ECMAScript you surely can define slave functions by nesting them into other functions, right?
…I won’t be the first to try this.