WHAT’S IN THIS CHAPTER?
World chess champion Magnus Carlsen was referring to chess, but he could have been talking about programming when he said: “[In] general good players use more long-term memory than short-term memory during a chess game. You use past experiences. It is the intuition that is largely based on the past experiences. So it is your experience that gives you a different impression of the new situations before you, and then you have to consider what impression you can use. You must be able to continuously make up your mind about which past experience can be used.” (http://www.worldchesschampionship2013.com/2013/11/secret-of-magnus-carlsens-chess.html
)
Good chess players strike with flashes of tactical brilliance at the chessboard, and good programmers can spin beautiful algorithms at the keyboard, but the best ones also draw on patterns learned from experience and “continuously make up their minds about which can be used.” A developer who draws on wisdom and experience from the past is likely to solve problems more economically and cleanly than one who must invent everything himself.
Patterns can range in scale from little idioms of the language (++
i) up to the structure of the system itself (n-tier architecture). The pattern of looping through an array is an example of the tiny variety, but illustrates how patterns accumulate and improve through experience and how code becomes cleaner and more reliable through their use.
The first version of looping through an array is so old-fashioned that it is not even valid JavaScript (containing, as it does, a goto
), but it is how programming was done before better patterns emerged.
ix=0;
the_test:
if (ix >= myArray.length) {
goto the_end;
}
doSomething(myArray[ix]);
ix = ix+1;
goto the_test; // Pretend JavaScript has a goto statement.
the_end:
The goto
statement led to code that was so hard to follow that its use became shunned. Shunning turned to banishment and now JavaScript doesn’t even have a goto
statement. A better pattern, the for
loop, was incorporated into the language:
for (ix=0; ix<myArray.length; ++ix ) {
doSomething(myArray[ix]);
}
The for
version is better because it is more concise and clear. Isn’t it easier to tell at a glance what the code is attempting to do? Doesn’t there seem to be much less room for error than in the goto
version?
Yet for
was not the last word. The accumulation of experience and patterns was not complete. Looping through elements in an array is so common that the designers of ECMAScript 5 have provided an even more concise formulation:
myArray.forEach(doSomething);
This is even clearer because the entire for
statement, with its initialization, test, and increment, has been replaced by the simple and obvious myArray.forEach
.
Not only is it more clear, but it’s more reliable. It is impossible to commit an off-by-one error in the condition because there is no condition. You can’t reference the wrong element in the array because there are no subscripts.
Of course, myArray.forEach
is only clear if you know what a forEach
statement is and you are comfortable with callbacks. That brings us to the first main point of this chapter: how a broader vocabulary can help you produce more elegant code.
It is a common misconception that code is simplest and easiest to understand when it employs only the most elementary concepts. On the contrary, an impoverished vocabulary results in longer, more tortuous programs.
Table 4-1 compares the for
and forEach
versions of the simple loop from the previous section.
Table 4.1 Comparing for and forEach
FOR | FOREACH | |
Assignments |
ix=0 ++ix |
N/A |
Property references | myArray.length | N/A |
Array-element references | myArray[ix] | N/A |
Conditional branching | ix<myArray.length | N/A |
Function calls | doSomething(myArray[ix]) | doSomething |
Developers are so used to for
loops that they may forget how much extra work is involved. The entire purpose of the code was to call doSomething
on each element of the array, yet all those assignments, property references, and branching were dragged in. How can code ever be elegant in the midst of so much useless clutter?
That example was on a small scale, and the pattern has been codified in the language itself, but the same is true for patterns on a larger scale that are expressed in your own code or third-party libraries. For example:
You can imagine how ugly, difficult, and inefficient your code would be if you were not acquainted with for,forEach
, or the other looping constructs in the language. What a step up it would be to start programming with ordinary for
statements! As the higher-level patterns in this book become part of your everyday vocabulary, your code will step up by an equal measure.
The seminal book Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley, 1994) has encouraged a generation of developers to construct reliable software by applying twenty-three patterns that have proven both useful and robust. As long as software is being written, those patterns will be employed.
However, song lyrics, when translated into a different language, often change their literal meaning in order to express the same emotion. If the beauty, rhythm, and rhyme are to be preserved, one must sing different words. The distinction made in Design Patterns between concrete and abstract classes has no literal translation to JavaScript, where everything is concrete and no classes exist. In the coming chapters, you will see how some of the classic patterns can find their expression in JavaScript.
You will also learn new patterns that are idiomatic to JavaScript and might not have such a prominent place in a classical language.
The patterns themselves are only half the story, however, and the second half at that. The first half is the testing story. (Remember: Test first!) Embarrassing experiences have taught all of us that we can mess up even the simplest pattern. The only way to verify that a pattern is correctly coded is to test it.
We have found that developers generally have a harder time knowing how to rigorously test code based on some of these patterns than learning how to code with the patterns themselves. Most of the rest of this book is therefore devoted to a test-first approach to learning various patterns, and an explication of how to proceed with a test-first approach to using them.
As you employ proven, elegant design patterns, and use best practices for testing your work, you will produce reliable code that gives you great satisfaction—both functionally and creatively. What more could a software developer want from his day job?
In software development, there are small-scale patterns in the syntax and idioms of the language, and large-scale patterns for constructing entire systems. Like a top-notch chess player who can see familiar patterns in every new position, a good software developer has a broad vocabulary of patterns at his or her disposal. The developer can use these patterns as guides for thought and as well-tested building blocks to construct reliable systems.
The next chapter covers one of the most frequent patterns in JavaScript: the callback.