It took a lot longer than I expected but this is first installment of my Design Principles To Live By series: Encapsulate Variation. This is a quick tour through the principles behind the design patterns. Following these allows will allow you to make the "right" choice in most situations. As with everything, there are exceptions to the rules. These are not set in stone. Violating them in okay, but only if you understand why doing so is better than following them.
A test for good software design is how well it can deal with future change. As the cliche truthfully claims, the only constant is change. Inevitably any piece of software the is in use will be asked to change. Business needs will evolve or the problem space will be better understood, etc. Whatever the reason, the software will need to change. A good design will allow for that change without too much work. A bad design will be very hard to modify.
While it is nearly impossible to predict the specifics of future requirements, it is much easier to understand what is likely to change. When designing software, look for the portions most likely to change and prepare them for future expansion by shielding the rest of the program from that change. Hide the potential variation behind an interface. Then, when the implementation changes, software written to the interface doesn’t need to change. This is called encapsulating variation.
Let’s look at an example. Let’s say you are writing a paint program. For your first version, you choose to only handle .bmp files because there is no compression and they are easy to load. You know that if the program becomes popular, you’ll want to load other files like .jpg, .gif, .png, etc. The naive way to implement the loading of a bmp is to write some functions that do just that. They load the bitmap file into your internal version of it. If you are using an api to load them, you might even be tempted to put the correct API calls directly in the Open button handler. Doing either will make life harder later. Every place that has to load the files (the button handler, the file recovery routine, the recently-used menu selections, etc.) will have to change when you add support for JPEGs and portable network graphics.
A better solution would be to create an interface IImageLoader and inherit from it for BMPLoader. Then all code handling loading files will call methods on IImageLoader and won’t care (or know) about the specifics of the type of image being loaded. Adding JPEGLoader and PNGLoader will require changing much less code. If done right, changes will be isolated to just one place.
The point of this principle is to look ahead a little. See what is likely to vary, and plan for it. Don’t plan for it by writing the handlers for JPEG. Maybe HDPhoto will have taken over the world by then. Rather, ensure that those things most likely to vary are encapsulated and therefore hidden from the rest of the program.