21. COMMAND and ACTIVE OBJECT: Versatility and Multitasking

image

© Jennifer M. Kohnke

No man has received from nature the right to command his fellow human beings.

—Denis Diderot (1713–1784)

Of all the design patterns that have been described over the years, COMMAND impresses me as one of the simplest and most elegant. But we shall see, the simplicity is deceptive. The range of uses that COMMAND may be put to is probably without bound.

The simplicity of COMMAND, as shown in Figure 21-1, is almost laughable. Listing 21-1 doesn’t do much to dampen the levity. It seems absurd that we can have a pattern that consists of nothing more than an interface with one method.

Figure 21-1. COMMAND pattern

image


Listing 21-1. Command.cs

public interface Command
{
  void Execute();
}


In fact, this pattern has crossed a very interesting line. And it is in the crossing of this line that all the interesting complexity lies. Most classes associate a suite of methods with a corresponding set of variables. The COMMAND pattern does not do this. Rather, it encapsulates a single function free of any variables.

In strict object-oriented terms, this is anathema, smacking of functional decomposition. It elevates the role of a function to the level of a class. Blasphemy! Yet at this boundary where two paradigms clash, interesting things start to occur.

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

image

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

image

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 commands—the wiring—in 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.


Transactions

The COMMAND pattern has another common use, one we will find useful in the payroll problem: the creation and execution of transactions. Imagine, for example, that we are writing the software that maintains a database of employees (see Figure 21-4). Users can apply a number of operations to that database, such as adding new employees, deleting old employees, or changing the attributes of existing employees.

Figure 21-4. Employee database

image

A user who decides to add a new employee must specify all the information needed to successfully create the employee record. Before acting on that information, the system needs to verify that the information is syntactically and semantically correct. The COMMAND pattern can help with this job. The command object acts as a respository for the unvalidated data, implements the validation methods, and implements the methods that finally execute the transaction.

For example, consider Figure 21-5. The AddEmployeeTransaction contains the same data fields that Employee contains, as well as a pointer to a PayClassification object. These fields and object are created from the data that the user specifies when directing the system to add a new employee.

Figure 21-5. AddEmployee transaction

image

The Validate method looks over all the data and makes sure that it makes sense. It checks it for syntactic and semantic correctness. It may even check to ensure that the data in the transaction is consistent with the existing state of the database. For example, it might ensure that no such employee already exists.

The Execute method uses the validated data to update the database. In our simple example, a new Employee object would be created and loaded with the fields from the AddEmployeeTransaction object. The PayClassification object would be moved or copied into the Employee.

Physical and Temporal Decoupling

The benefit this give us is in the dramatic decoupling of the code that procures the data from the user, the code that validates and operates on that data, and the business objects themselves. For example, one might expect the data for adding a new employee to be procured from a dialog box in a GUI. It would be a shame if the GUI code contained the validation and execution algorithms for the transaction. Such a coupling would prevent that validation and execution code from being used with other interfaces. By separating the validation and execution code into the AddEmployeeTransaction class, we have physically decoupled that code from the procurement interface. What’s more, we’ve separated the code that knows how to manipulate the logistics of the database from the business entities themselves.

Temporal Decoupling

We have also decoupled the validation and execution code in a different way. Once the data has been procured, there is no reason why the validation and execution methods must be called immediately. The transaction objects can be held in a list and validated and executed much later.

Suppose that we have a database that must remain unchanged during the day. Changes may be applied only during the hours between midnight and 1 A.M. It would be a shame to have to wait until midnight and then have to rush to type all the commands in before 1 A.M. It would be much more convenient to type in all the commands, have them validated on the spot, and then executed later, at midnight. The COMMAND pattern gives us this ability.

Undo Method

Figure 21-6 adds the Undo() method to the COMMAND pattern. It stands to reason that if the Execute() method of a Command derivative can be implemented to remember the details of the operation it performs, the Undo() method can be implemented to undo that operation and return the system to its original state.

Figure 21-6. Undo variation of the COMMAND pattern

image

image

Imagine, for example, an application that allows the user to draw geometric shapes on the screen. A toolbar has buttons that allow the user to draw circles, squares, rectangles, and so on. Let’s say that the user clicks the Draw Circle button. The system creates a DrawCircleCommand and then calls Execute() on that command. The DrawCircleCommand object tracks the user’s mouse, waiting for a click in the drawing window. On receiving that click, it sets the click point as the center of the circle and proceeds to draw an animated circle at that center, with a radius that tracks the current mouse position. When the user clicks again, the DrawCircleCommand stops animating the circle and adds the appropriate circle object to the list of shapes currently displayed on the canvas. It also stores the ID of the new circle in a private variable of its own. Then it returns from the Execute() method. The system then pushes the expended DrawCirlceCommand on the stack of completed commands.

Some time later, the user clicks the Undo button on the toolbar. The system pops the completed commands stack and calls Undo() on the resulting Command object. On receiving the Undo() message, the DrawCircleCommand object deletes the circle matching the saved ID from the list of objects currently displayed on the canvas.

With this technique, you can easily implement Undo in nearly any application. The code that knows how to undo a command is always right next to the code that knows how to perform the command.

Active Object

One of my favorite uses of the COMMAND pattern is the ACTIVE OBJECT pattern.1 This old technique for implementing multiple threads of control has been used, in one form or another, to provide a simple multitasking nucleus for thousands of industrial systems.

The idea is very simple. Consider Listings 21-2 and 21-3. An ActiveObjectEngine object maintains a linked list of Command objects. Users can add new commands to the engine, or they can call Run(). The Run() function simply goes through the linked list, executing and removing each command.


Listing 21-2. ActiveObjectEngine.cs

using System.Collections;

public class ActiveObjectEngine
{
  ArrayList itsCommands = new ArrayList();

  public void AddCommand(Command c)
  {
    itsCommands.Add(c);
  }

  public void Run()
  {
    while (itsCommands.Count > 0)
    {
      Command c = (Command) itsCommands[0];
      itsCommands.RemoveAt(0);
      c.Execute();
    }
  }
}



Listing 21-3. Command.cs

public interface Command
{
  void Execute();
}


This may not seem very impressive. But imagine what would happen if one of the Command objects in the linked list put itself back on the list. The list would never go empty, and the Run() function would never return.

Consider the test case in Listing 21-4. This test case creates a SleepCommand, which among other things passes a delay of 1,000 ms to the constructor of the SleepCommand. The test case then puts the SleepCommand into the ActiveObjectEngine. After calling Run(), the test case expects that a certain number of milliseconds have elapsed.


Listing 21-4. TestSleepCommand.cs

using System;
using NUnit.Framework;

[TestFixture]
public class TestSleepCommand
{
  private class WakeUpCommand : Command
  {
    public bool executed = false;


    public void Execute()
    {
      executed = true;
    }
  }

  [Test]
  public void TestSleep()
  {
    WakeUpCommand wakeup = new WakeUpCommand();
    ActiveObjectEngine e = new ActiveObjectEngine();
    SleepCommand c = new SleepCommand(1000, e, wakeup);
    e.AddCommand(c);
    DateTime start = DateTime.Now;
    e.Run();
    DateTime stop = DateTime.Now;
    double sleepTime = (stop-start).TotalMilliseconds;
    Assert.IsTrue(sleepTime >= 1000,
      "SleepTime " + sleepTime + " expected > 1000");
    Assert.IsTrue(sleepTime <= 1100,
      "SleepTime " + sleepTime + " expected < 1100");
    Assert.IsTrue(wakeup.executed, "Command Executed");
  }
}


Let’s look at this test case more closely. The constructor of the SleepCommand contains three arguments. The first is the delay time, in milliseconds. The second is the ActiveObjectEngine that the command will be running in. Finally, there is another command object called wakeup. The intent is that the SleepCommand will wait for the specified number of milliseconds and will then execute the wakeup command.

Listing 21-5 shows the implementation of SleepCommand. On execution, SleepCommand checks whether it has been executed previously. If not, it records the start time. If the delay time has not passed, it puts itself back in the ActiveObjectEngine. If the delay time has passed, it puts the wakeup command into the ActiveObjectEngine.


Listing 21-5. SleepCommand.cs

using System;

public class SleepCommand : Command
{
  private Command wakeupCommand = null;
  private ActiveObjectEngine engine = null;
  private long sleepTime = 0;
  private DateTime startTime;
  private bool started = false;

  public SleepCommand(long milliseconds, ActiveObjectEngine e,
                      Command wakeupCommand)

  {
    sleepTime = milliseconds;
    engine = e;
    this.wakeupCommand = wakeupCommand;
  }

  public void Execute()
  {
    DateTime currentTime = DateTime.Now;
    if (!started)
    {
      started = true;
      startTime = currentTime;
      engine.AddCommand(this);
    }
    else
    {
      TimeSpan elapsedTime = currentTime - startTime;
      if (elapsedTime.TotalMilliseconds < sleepTime)
      {
        engine.AddCommand(this);
      }
      else
      {
        engine.AddCommand(wakeupCommand);
      }
    }
  }
}


We can draw an analogy between this program and a multithreaded program that is waiting for an event. When a thread in a multithreaded program waits for an event, the thread usually invokes an operating system call that blocks the thread until the event has occurred. The program in Listing 21-5 does not block. Instead, if the event it is waiting for (elapsedTime.TotalMilliseconds < sleepTime) has not occurred, the thread simply puts itself back into the ActiveObjectEngine.

Building multithreaded systems using variations of this technique has been, and will continue to be, a very common practice. Threads of this kind have been known as run-to-completion tasks (RTC); each Command instance runs to completion before the next Command instance can run. The name RTC implies that the Command instances do not block.

The fact that the Command instances all run to completion gives RTC threads the interesting advantage that they all share the same runtime stack. Unlike the threads in a traditional multithreaded system, it is not necessary to define or allocate a separate runtime stack for each RTC thread. This can be a powerful advantage in memory-constrained systems with many threads.

Continuing our example, Listing 21-6 shows a simple program that makes use of SleepCommand and exhibits multithreaded behavior. This program is called DelayedTyper.


Listing 21-6. DelayedTyper.cs

using System;

public class DelayedTyper : Command
{
  private long itsDelay;
  private char itsChar;
  private static bool stop = false;
  private static ActiveObjectEngine engine =
    new ActiveObjectEngine();

  private class StopCommand : Command
  {
    public void Execute()
    {
      DelayedTyper.stop = true;
    }
  }

  public static void Main(string[] args)
  {
    engine.AddCommand(new DelayedTyper(100, '1'));
    engine.AddCommand(new DelayedTyper(300, '3'));
    engine.AddCommand(new DelayedTyper(500, '5'));
    engine.AddCommand(new DelayedTyper(700, '7'));

    Command stopCommand = new StopCommand();

    engine.AddCommand(
      new SleepCommand(20000, engine, stopCommand));
    engine.Run();
  }

  public DelayedTyper(long delay, char c)
  {
    itsDelay = delay;
    itsChar = c;
  }

  public void Execute()
  {
    Console.Write(itsChar);
    if (!stop)
      DelayAndRepeat();
  }

  private void DelayAndRepeat()
  {
    engine.AddCommand(
      new SleepCommand(itsDelay, engine, this));
  }
}


Note that DelayedTyper implements Command. The Execute method simply prints a character that was passed at construction, checks the stop flag and, if not set, invokes DelayAndRepeat. The DelayAndRepeat constructs a SleepCommand, using the delay that was passed in at construction, and then inserts the SleepCommand into the ActiveObjectEngine.

The behavior of this Command is easy to predict. In effect, it hangs in a loop, repeatedly typing a specified character and waiting for a specified delay. It exits the loop when the stop flag is set.

The Main program of DelayedTyper starts several DelayedTyper instances going in the ActiveObjectEngine, each with its own character and delay, and then invokes a SleepCommand that will set the stop flag after a while. Running this program produces a simple string of 1s, 3s, 5s, and 7s. Running it again produces a similar but different string. Here are two typical runs:

135711311511371113151131715131113151731111351113711531111357...
135711131513171131511311713511131151731113151131711351113117...

These strings are different because the CPU clock and the real-time clock aren’t in perfect sync. This kind of nondeterministic behavior is the hallmark of multithreaded systems.

Nondeterministic behavior is also the source of much woe, anguish, and pain. As anyone who’s worked on embedded real-time systems knows, it’s tough to debug nondeterministic behavior.

Conclusion

The simplicity of the COMMAND pattern belies its versatility. COMMAND can be used for a wonderful variety of purposes, ranging from database transactions to device control to multithreaded nuclei to GUI do/undo administration.

It has been suggested that the COMMAND pattern breaks the OO paradigm by emphasizing functions over classes. That may be true, but in the real world of the software developer, usefulness trumps theory. The COMMAND pattern can be very useful.

Bibliography

[GOF95] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995

[Lavender96] R. G. Lavender and D. C. Schmidt, “Active Object: An Object Behavioral Pattern for Concurrent Programming,” in J. O. Coplien, J. Vlissides, and N. Kerth, eds. Pattern Languages of Program Design, Addison-Wesley, 1996.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
44.200.40.97