Simple Commands


Several years ago, I consulted for a large firm that made photocopiers. I was helping one of its development teams with the design and implementation of the embedded real-time software that drove the inner workings of a new copier. We stumbled on the idea of using the COMMAND pattern to control the hardware devices. We created a hierarchy that looked something like Figure 21-2.

Figure 21-2. Some simple commands for the copier software


The role of these classes should be obvious. Call Execute() on a RelayOnCommand turns on a relay. Calling Execute() on a MotorOffCommand turns off a motor. The address of the motor or relay is passed into the object as an argument to its constructor.

With this structure in place, we could now pass Command objects around the system and Execute() them without knowing precisely what kind of Command they represented. This led to some interesting simplifications.

The system was event driven. Relays opened or closed, motors started or stopped, and clutches engaged or disengaged, based on certain events that took place in the system. Many of those events were detected by sensors. For example, when an optical sensor determined that a sheet of paper had reached a certain point in the paper path, we'd need to engage a certain clutch. We were able to implement this by simply binding the appropriate ClutchOnCommand to the object that controlled that particular optical sensor. See Figure 21-3.

Figure 21-3. A command driven by a sensor


This simple structure has an enormous advantage. The Sensor has no idea what it is doing. Whenever it detects an event, it simply calls Execute() on the Command that it is bound to. This means that the Sensors don't have to know about individual clutches or relays. They don't have to know the mechanical structure of the paper path. Their function becomes remarkably simple.

The complexity of determining which relays to close when certain sensors declare events has moved to an initialization function. At some point during the initialization of the system, each Sensor is bound to an appropriate Command. This puts all the logical interconnections between the sensors and commandsthe wiringin one place and gets it out of the main body of the system. Indeed, it would be possible to create a simple text file that described which Sensors were bound to which Commands. The initialization program could read this file and build the system appropriately. Thus, the wiring of the system could be determined completely outside the program and could be adjusted without recompilation.

By encapsulating the notion of a command, this pattern allowed us to decouple the logical interconnections of the system from the devices that were being connected. This was a huge benefit.

Where'd the I go?

In the .NET community, it is conventional to precede the name of an interface with a capital I. In the preceding example, the interface Command would conventionally be named ICommand. Although many .NET conventions are good, and in general this book follows them, this particular convention is not favored by your humble authors.

In general, it is a bad idea to pollute the name of something with an orthogonal concept, especially if that orthogonal concept can change. What if, for example, we decide that ICommand should be an abstract class instead of an interface? Must we then find all the references to ICommand and change them to Command? Must we then also recompile and redeploy all the affected assemblies?

This is the twenty-first century. We have intelligent IDEs that can tell us, with just a mouse-over, whether a class is an interface. It is time for the last vestiges of Hungarian notation to finally be put to rest.





Agile Principles, Patterns, and Practices in C#
Agile Principles, Patterns, and Practices in C#
ISBN: 0131857258
EAN: 2147483647
Year: 2006
Pages: 272

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net