Upfront, about any examples: Yes, bad examples can be found in many places. But bad examples of all in one proc code can also be found. You can do bad stuff with any coding paradigm. That isn't a flaw of the paradigm.
And, in case you say some are more likely to be gotten wrong. Well, some may require more study how to do them. Doesn't make the paradigm less good. It may even be better. (just not for someone who does their first hello world with it).
Lot's of interesting points there that lead to some questions:
1. What's easier ? a) to read 1000 lines of code in a single block or b) read 20 out of line functions/procedures that implement that one "macro" function ?
2. Isn't the fact that the programmer's attention has to jump 20 times (or at least to 20 different places) to the "sub-functions" a form of spaghetti code ?
The first reads a bit like a "trick question"...
But I actually already answered it:
A well named procedure call is much easier to read that embedded code,
If you find code that says
MemberList.SortByKey(skLastName);
Do you really need to jump there and read that code?
If I program an Engine, I can embed the code to measure the position, to open the fuel valves, to ignite the spark-plugs... Or I can write
If GetPosition(...)= CONST_TRIGGER_VALVE then OpenFuellValve
else
If GetPosition(...)= CONST_TRIGGER_SPARK then Ignite;
Sure, if (which is likely not that often) I need to read how to do those subtasks, then I need to jump around. But on the plus site, if I just need to work on the big picture, that is so much easier to read and work with.
And, well, if there is a bug somewhere, and I don't know in which routine? (Ignoring test cases, and test-ability improvements by having some code extracted), I can on the first run step over each function and check the result, until I find the routine that returns wrong.
So, having stuff in functions is much easier to handle. Even for someone who doesn't know the code, as they can decide which blocks to read and which not, simple by the information that the function name itself gives them.
And I don't see this in any way fulfilling the criteria for Spaghetti code. If anything it adds structure, hence reduces the spaghetti factor. It is also likely to reduce nested conditions, loops and sets clear bounds for conditional code flow, which also reduces the spaghetti factor.
3. Isn't the fact that the procedures being called from only one place is highly misleading ? after all, functions/procedures can easily be interpreted as being code that is shared among other functions/procedures,
They can, a lot of thinks can be seen in specific ways, and in others.
That you can see something one way, doesn't mean that is the essence of it.
Its a named block of code (with certain other details about it...).
That it can be re-used is one of its qualities, and one that is very often used. But not exclusive.
Encapsulation can also be a reason. You can have local vars in that procedure (including static writeable const) that can not be seen from anywhere else.
And just naming a block of code, and expressing that it does a task that is distinct enough to qualify for being separated, is a good reason too. It gives very valuable info to the reader.
the fact that they aren't shared is misleading and makes program maintenance harder (the programmer has to establish that the function/procedure is executed from only 1 place in order to ensure that any changes to it don't affect other code, this task would not be necessary if the code was not in a separate function/procedure. Note: nesting does _not_ solve that problem.)
What problem? I don't see it misleading.
Also there has to be differentiated between
1) it is only called from one place (just because nothing else needs to call it.
2) it must be called from no other place
I never said that such code may be of the 2nd case.
And in case of 1, its just a what it is, if the reader thinks its called from other places too, then there is no harm at all. None, nada, zero.
But talking about such code (which is new, which is not my original suggestion)There is danger too, if such code is inlined. It can be copy/pasted. Among other things. Adding a comment to protect it is hard, because you need a marker were that code block ends, and that marker must not accidentally move if surrounding code is edited.
A highly scoped nested proc, with the same comment, and a name that makes it very clear not to re-use it elsewhere => that can be a better approach. But that is very much case by case....
4. doesn't the fact that the programmer has to remember whether or not each of those functions or procedures cause side effects make maintenance and understanding more difficult ?
Again, a new implication, that wasn't in my original descriptionBut fair enough, such code may have side effects, and I would still outline it.
In the above example "OpenFuelValve" would have side effects, and it would be very clear what they are. And if the code does exactly what the name says, then there is nothing misleading, nothing to remember,....
In fact "OpenFuelValve" is much better than 20 lines of code, containing (somewhere in those 20 lines) API calls such as
vh := GetValveHandle;
SendCommand(vh, CmdChange, GetCurrentOffset + value_open)
And all that code would be surrounded by other code doing other stuff, leaving it completely unclear what it does, until you read the "fine print".
Sure you could have a big comment in front of it. But where does the code end, that is covered by the comment? A function call is much clearer.
5. if the programmer forgets some detail while reading the "macro"/"composite" function, isn't the fact that he/she now has to jump back to some function/procedure to remember the detail in addition to having to (hopefully) remember which function/procedure has the detail in question, make the maintenance and understanding of the whole harder ? Isn't this one of typical problems associated with spaghetti code ?
How does having to read copious amount of extra lines every time you need the bigger picture solve that. Keeping the code inlined just increases the likelihood to overlook parts of it.
Also again, they should be appropriately named. They should just do one thing and that is in the name and hence no way to forget it.