Click here to Skip to main content
Click here to Skip to main content

Task Pattern meets the Command Pattern

, 16 Sep 2005
Rate this:
Please Sign up or sign in to vote.
Communicating across a network can be time consuming manifesting itself as an unresponsive user interface. This article tries to solve the problem.

Introduction

Communicating across a network can be time consuming manifesting itself as an unresponsive user interface. Avoiding long and non-deterministic operations on the UI thread will not increase overall performance but it will give the user some degree of confidence that the application hasn’t silently died.

One of my earlier solutions (see my earlier article: Threading and the UI) called for encapsulating the long running operation within a Task class. The Task would in turn fire an event to notify the caller of its completion. Unfortunately, this implementation had very tight coupling between the requested operation and the mechanics of the task used to prepare, invoke and return information from the spawned thread.

It soon became clear that a more robust solution was needed, which would decouple the threading control of the Task class and whatever operation it was supposed to control.

A more generic Task Pattern Design

The decoupling I was looking for was achieved by abstracting away the “requested operation” details and encapsulating them into an object that could be operated upon without having to know any of the request details. Sounds a lot like the Command Pattern and that is exactly what the solution is modeled after.

Command Pattern in Review

The basic intent of the Command Pattern is to encapsulate a request inside an object. The Command Pattern allows you to decouple the requester of an action from the object that actually performs the action. The action and the receiver may be bound together within the command object which in turn is exposed through the Execute method of the Command interface. Another point worth noting is that the invoker of the command is also unaware of how the work is done. The invoker (our Task object) simply calls Execute on the Command object at some future point in time without needing to know what type of processing is to be done.

The pattern provides quite a bit of flexibility in regards to how much or how little logic you design into the concrete command. From the implementation point of view you can have two types of concrete command classes. Forwarding Commands are those commands that simply call a member function within a Receiver object. Concrete commands may also embed logic, which goes well beyond simple delegation. These are referred to as Active Commands. Active Commands can go so far as to service the entire request without the need for an embedded receiver. My rendition of the Task pattern is a blend of the two concepts. The concept of a reader object must be known by the concrete command. The reader will in turn abstract away derived class details in an attempt to maintain a loose coupling.

The Task-Command Pattern

The Task pattern encapsulates a command object. It provides an interface by which to load the command object, subscribe to command completion events and a means by which to invoke the processing of the command asynchronously.

public class Task
{
 private Thread     _thrd;
 private Command _cmd;

 public delegate void TaskPassed();
 public delegate void TaskFailed();

 public event TaskPassed passed;
 public event TaskFailed failed;

 public void setCommand(Command cmd)
 public void RunAsynchTask()
}












Concrete Command Class

In the sample code, we bind together a SQL statement with a database reader object. The concrete command (SelectCommand) Execute method simulates a somewhat lengthy database fetch.

The fetch instructs the reader to perform the requested SELECT command expecting back an ArrayList.

public class Command
{
    public Command(){}
    public virtual void execute() {}
    public virtual bool passed() {return false;}
}

public class SelectCommand : Command
{
    private DataAccess.DBReader _reader;
    private bool _passed;
    private string _sp;
    private ArrayList _al;

    public SelectCommand(DataAccess.DBReader rdr, string sp)
    {
        _reader = rdr;
        _sp = sp;
    }
    public override void execute()
    {
        // I take a long time to complete
        Thread.Sleep(5000);

        _al = _reader.select(_sp);

        // determine if the command has been successfully executed
        _passed = _reader.selectPassed();

    }

The SelectCommand object stores the resultant ArrayList, which can be queried by the Task initiator through a public member function.

Wiring things up

To use the Task class we first instantiate the object on the heap and wire up to the event notifications. The Task provides an event signaling that processing was successful, and an event which denotes the occurrence of a failure.

      // initialize asynch task
      // request successful completion notification
      Task task = new Task();
      task.passed += 
        new WinFormTaskPattern.Task.TaskPassed(this.ListBoxSelectPassed);

      DataAccess.DBReader reader = new DataAccess.GroupDB();

      cmd = new SelectCommand(reader, "A Select Stored Proc");
      task.setCommand(cmd);

      beginListBoxInitialization(this.lstBox, this.btnRefresh);
      task.RunAsynchTask();

Once the Task object has been wired-up, we then tell it what command it will process. During the setup of the command, we associate an applicable reader object. At this juncture all that remains is to tell the Task to process.

Summary

In this article I incorporated the Command Design Pattern in order to provide a higher degree of reusability. The delegate mechanism of the CLR simplifies greatly the work required in separating UI thread operations from long running processes. Using these mechanisms through higher-level design patterns such as the one I described should seriously be considered in any GUI that must communicate across the wire. That of course is just my opinion.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Gary J. Kuehn
Engineer
United States United States
No Biography provided

Comments and Discussions

 
QuestionWhy Two Events? PinmemberJosh Smith17-Sep-05 16:05 
AnswerRe: Why Two Events? PinmemberGary J. Kuehn18-Sep-05 3:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 16 Sep 2005
Article Copyright 2005 by Gary J. Kuehn
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid