I’ve recently been reading an article (for “article” read “web-site”!) on Extreme Programming (XP for short). One of the tenets of this philosophy is to write only as much code as you need. This specifically inhibits adding a feature to a class (or program or library) if the application for which you are developing doesn’t need it now.
The last edition of Overload had an entire interview with Kent Beck, the brain behind the brawn of XP, but here's a quick jump - “You Aren’t Gonna Need It”. [http://c2.com/cgi/wiki?YouArentGonnaNeedIt]
Now, I’m what you might call a novice C++ programmer, inasmuch as I do it for a living and I reckon I’m not bad (!) but I’ve a lot to learn. Definitely. No doubt on that at all. In fact, I’m so convinced of the latter that ACCU’s bandwidth is much abused by me reading book reviews (maybe I should download it for offline...) and Amazon probably consider me a personal friend ;-) I did get a kind of virtual Christmas card. But...
I read everywhere (particularly “Effective C++ CD” [Meyers, 1999]) that I should program in “the future tense”. If a facility may be required at a future date, then at least prepare for the possibility, if not actually provide it, now. The code should be graceful in the event of change, which to my mind precludes the necessity of hacking at the code internals to get some new feature to work.
So, I have conflicting views - don’t I? Well, this is one of those “yes and no” questions, I think. Programming increasingly appears to me to consist almost entirely of such things. I get answers - which raise yet more questions.
So, back to You Aren’t Gonna Need It. The principle behind this seems to me to be in two parts. There’s the KISS part (Keep It Small & Simple) and the, er, lazy part. That’s lazy as in don’t do it if you don’t need to. Both of these are principles I can relate to! But I want to write better programs too, that respond well to change and evolution. Can these goals be reconciled?
Enter the Strategy.
This is a pattern I first encountered in “Design Patterns - Elements of Re-Usable Software” [Gamma, et. al.1995]. In there Strategy is described as follows:
“Define a family of algorithms ... and make them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.”
The principles of “You Aren’t Gonna Need It” say quite clearly - if you don’t need it, don’t write it. The principles of “Good Design” say quite clearly - program with change in mind. By using the Strategy pattern today, we can satisfy much of both these requirements. Compromise, as always, is necessary.
To make this more concrete I will describe the problem that faced me which first brought these two principles into conflict.
A program was required to read a text file containing records, select certain records based on a fairly strict set of requirements, and write those records in a different format to a new file. Being a very simple specification, it was of course required the day before. Immediately and implicitly, XP reared its head. After a brief design stage a solution immediately presented itself, but closer inspection showed a flaw in the design.
The specifications for selecting a record were quite strict, along the lines of “if this field has such and such a sequence at its end, select the record.” It amounted to choosing a record by its “type”, for want of a better word. The flaw in the design was this (you’ve all seen it coming, I know) - what if the selection requirements change? And from this one revelation came more “what if?”s - what if the output format changes? What if the input format changes? What if the record layout changes? And worse - what if we want several input formats, output formats, record layouts ...
The Strategy (Strategic?) solution seemed a good one. Look for the varying elements of the design and factor them out. Straight from “Design Patterns...” I was pleased with myself. The varying elements were:
Input File format
Output file format
Record selection criteria
A Strategy, then, for each one, allowing the details of each element’s implementation to be changed at will. In the first instance, each strategy would have only one implementation (XP) but could be extended with little effort later on. Adding the extra layer of support needed to implement the strategies (the compromise) was fairly simple - an abstract base class for each Strategy, a concrete Strategy to implement the details for each one, and that’s all.
The principle is simple. A base class which represents the strategy. In this case, there will be a ReaderStrategy, a WriterStrategy, FormatStrategy and QueryStrategy. Derived from each is a concrete strategy which represents the algorithm to be used, hence, for example, CSVReader, FixedWidthWriter, Type1Format, LocalCustQuery.
Type1Format could mean anything - in my case, it describes the field widths and data types of the fields in the file.
I also introduced a middle tier in the program as an interface between the details of the strategic portion and the user interface. This tier was to most intents and purposes the Handle of a Handle/Body construct, and as such held a pointer to each of the abstract strategies. It was implemented as a namespace, not a class, however.
This architecture allows new methods of, say, reading a file to be used without affecting anything else in the code. A new class, TabDelimitedReader, is derived from ReaderStrategy, and plugged in. In the simplest case, the client program doesn’t even need to know. One of the shortcomings of the strategy pattern as noted in Design Patterns, is that client code needs to know the types of concrete strategies in use. By introducing the middle tier I removed even this requirement.
I have satisfied the requirements of my conflicting design paradigms - with reservations. To recap, You Aren’t Gonna Need It says don’t implement a feature if you don’t require it now. We have not implemented any features we don’t need. Programming for the Future states that the design should be adaptive to change. By using independent Strategies to implement the details of the program, the algorithms can be not only modified, but extended.
The use of Abstract Base Classes to define interfaces to the Strategies is also no accident. It enforces a particular interface on any future derivations, so existing code should continue to work, and also has elements of programming for the future in that inheritance is there already - it doesn’t require changes to existing code to get it to work tomorrow.
One last bonus to all this. The program is a Windows program. One of the possibilities I “foresaw” was the ability to change the record selection criteria from a menu. The design even allows for this (it doesn’t do it at the moment) because the implementations can be varied at run time. It can be supported if it is feasible (in this simple case, it is) to open up the details of available strategies to client code.