loading...

Literary Criticism and Design Patterns

lytecyde profile image Mik Seljamaa 🇪🇪 ・5 min read

There is an important difference between intention and action. A scriptwriter might intend to tell us that the bad guy is horrible, and will do it by writing scenes involving nasty deeds. Our intention might be to signal that a memory page in the cache is no longer valid, our action is to set the dirty flag.

To starkly expose this point, consider an assembly language. An opcode might perform the most peculiar settings of the processor's outputs given the inputs, but we think of the opcode by its mnemonic, say DAA (Decimal Adjust Accumulator). Even though there is an identity between opcode and mnemonic, the high-level intention of the mnemonic can mask the action of the opcode on the accumulator, which just flips bits according to an algorithm. If we see the processing opportunities in the opcode, are we `abusing' it? The answer depends on the circumstance.

Whenever we have a distinction between intention and action, we have the opportunity to look at the effectiveness of the action and ask what we can learn about the intent, or the domain of the intent, from the structure of the selected action. Might another action have been better? Do problems in the action reveal issues in the intention? When we do this with books it is called literary criticism and taken seriously. If we are to learn how to write better programs, we need to learn as much as possible about our kind of lit crit, because that's the only way we'll be able to have a sensible discussion of the interplay of structure and detail that characterizes style. The really nice thing is, unlike prose lit crit, program lit crit is informed by experimental evidence such as failure reports. This ups the gusto and cuts the waffle, leaving the learning enhanced.

We can get a rigorous and elegant coding discipline out of the difference between intention and action. Consider the following fragment:

// Search the list of available dealers and find those that
// handle the triggering stock. Send them notification of
// the event.

for(DealerIterator DI(DealersOnline); DI.more(); DI++)
if(DI.CurrentDealer()->InPortfolio(TheEvent.GetStock()))
DI.CurrentDealer()->HandleEvent(TheEvent);

The definition of the objects has allowed the intention of the use case to be expressed succinctly. However, there is really no smaller granularity where we can cluster intention into comment and action into code without the comments getting silly.

If we interleave comment and code at this natural level of granularity, we can ensure that all lines in the program are interpreted in the comments. We are motivated to design objects (or functions) that we can use economically in this way. We find it easier to correct some inelegance than to explain it away.

By being conscious of the difference between intention and action, we can make both simultaneously economical, and fulfil the goals of a detailed design document's pseudo-code and an implementation's comments, while helping the implementation's verifiability. By putting everything in one place, we assist the coherence of the layers.

This concept is taken further in Donald Knuth's idea of `Literate Programming', which to be done well, really needs tool support from systems like his Web environment (predating the World Wide Web). But you don't need to buy all the gear to enjoy the sport - literate programming is more an attitude than a tool.

It is at this level of programming lit crit that we can seriously benefit from studying design patterns. These are chunks of architectural technique more complex than the usual flow control, stream management with error handling and other typical kinds of an idiom. They are extremely powerful and very portable. See the wonderful book by Gamma, Helm, Johnson, and Vlissides, where they describe a pattern as something that:

`... describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use the solution a million times over, without ever doing it the same way twice.' 

The theme that underlies all the issues discussed in this section is Aesthetic Quality. We all know a mess when we see one, but too often we are in danger of being paralysed, unable to act on the evidence of our own senses because there is no procedural translation of `It works but it's ugly.' When an experienced professional feels aesthetic disquiet and cares enough to say so, we should always take notice. Our standards of beauty change from generation to generation, and for some reason always follow function. That is why making code beautiful exploits a huge knowledge base that we may not have consciously integrated, and leads to cost-effective solutions. The stuff is less likely to incur vast maintenance costs downstream if it's beautiful. That's what beauty is. Aesthetic quality is probably the only criterion against which one can honestly argue that the wrong language has been used. An attempt to do an impressionist dawn in acrylic would be horrible even if the airbrush work were perfect.

We should be willing to look at the source code we produce not as the end product of a more interesting process, but as an artefact in its own right. It should look good stuck up on the wall. The up-front costs of actually looking at our code, and exploiting the mapping of the geometrical patterns of black and white, the patterns in the syntax, and the patterns in the problem domain aren't that great, given that they sometimes literally let you spot bugs from six feet away.

With all this literary criticism, what of religious wars? Some of it is done for the entertainment of course, and we don't want to impede the pleasure of ridiculing the peculiarities of our friends' favourite tools and techniques! But sometimes intelligent programmers get caught in distressing and counter-productive squabbles that just go around in circles. We forget that we can use structured arguments rigorously between ourselves. When an unwanted religious war breaks out, ask the following questions:

What is the global position that includes both special cases?
Is there a variation in intention between the positions?
What is the overall objective?

For example, you value the facilities of a powerful integrated environment. You use Emacs at work and have extended it to control your coffee machine. I use many machines, and bizarre though it may be, I know vi will always be there. We install Emacs on new sites and teach vi to novices. Your LISP technique, of course, sucks.

This evaluation of options against objectives often produces a genuine convergence of opinion among experienced people. Agreeing on the best idioms to get a job done in a well-understood environment does not mean that everyone is coerced to conform to it - they just agree. Contrary to popular opinion there often is a right answer. Talking to an experienced person about a new environment's idioms can teach you an awful lot very quickly while using the idioms from your old environment in a new one can lead to fighting all the way.

Copyright (c) Alan G Carter and Colston Sanger 1997

Posted on by:

Discussion

markdown guide