- In the Beginning, There Were Bits
- Structured Programming
- The Next Level Up: Functions and Procedures
- Classes: Beyond Structured Programming
- Conclusion: Abstraction Costs and Benefits
The Next Level Up: Functions and Procedures
Statements and structured code can be thought of as in terms of assembly language operations, at a higher level of abstraction.
The next level of abstraction up is to group statements into operational units with contracts of their own. Statements are collected to form functions, procedures, subroutines, or methods, as they're called in various languages. The nice thing about functions is that they limit even further the amount of code that you have to look at in order to understand a piece of code.
/** Takes the array and returns a sorted version, * removing duplicates. It may return the same array, or it * may allocate a new one. If there are no duplicates, it'll * probably return the old array. If there are, it'll have to * create a new one. */ int[] sort(int[] array) { ... the body ... }
You can learn a lot about the function without even seeing the body. The name sort, and the fact that it takes an array of integers and returns a (possibly different) array of integers, tell you a lot about what the function is supposed to do. The rest of the contract is described in the comment, which talks about other things such as memory allocation. That's actually even more important in C and C++ than in Java, where it's often up to the contract to express who's responsible for freeing memory allocated in the function.
The maintenance programmer's life is made simpler because the program is chopped up into these functional units. A common rule is that a function should be only as long as a screenful of code. That makes it possible to visualize, all at once, a complete, nameable, understandable unit of the program you're maintaining. That rule turns out to be a little bit too strict for me, but I follow it as often as I can.
The names of functions and procedures are a critical part of the abstraction. It takes a chunk of code and allows you to refer to it later with a single word (or a short collection of words, strungTogetherLikeThis or _like_this). This strategy focuses every line in the function on achieving the goal with that name. Once the scope of your function grows beyond the name you've assigned to it, it's time to consider breaking the function into pieces with better names. If you find yourself writing code like this:
void sortNamesAndSendEmail() { // Sort the names ... Spend 100 lines sorting the names ... // Send email ... Spend 500 lines sending out email ... }
it's a good indicator that it's time to start breaking the function into pieces. In effect, you'll probably write two functions:
sortNames() sendEmail()
which allows you to eliminate the verbose and weird function name sortNamesAndSendEmail.