Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#

Using the Chain Of Command Design Pattern Concepts to Perform Validation and Processing of Complex Data

Rate me:
Please Sign up or sign in to vote.
4.75/5 (9 votes)
3 Mar 2011CPOL7 min read 124.2K   861   49   9
An article demonstrating the usage of Chain of Command Design Pattern concepts to perform validation and processing of complex data.

Introduction

This article demonstrates how the usage of Chain of Command Design Pattern concepts can be used in the validation and processing of complex data.

Background

The Chain Of Command Design pattern is well documented, and has been successfully used in many software solutions.

In brief, this pattern involves a sequence of loosely coupled programming units, or handler objects. These objects are coupled together to form the links in a chain of handlers.

Each handler performs its processing logic, then potentially passes the processing request onto the next link (i.e. handler) in the chain. A client using the chain will only make one request for processing.  After this request, the chain handlers work to do the processing.

Benefits of the Chain Of Command Design Pattern

  • Complex processing algorithms can be simplified by breaking down logical units of work, and placing each unit of work in a chain handler.
  • Clients are de-coupled from the chain mechanism. Clients using the chain do not have to know specifics of handler processing logic, the structure of the links that make up the chain, nor the individual programming unit that will handle a request.
  • Handlers are isolated processing units and are loosely coupled, meaning that one handler does not have knowledge of other handler logic.
  • Chains can be dynamically constructed and modified to meet varying requirements.

Notes on Traditional Implementation of the Pattern

  • Only one handler in the chain handles a request.
  • Some requests may not get handled by any handler in the chain.
  • Many handlers are implemented in a linked list fashion, where one handler which has not handled a request, chooses to invoke the next handler in the chain.

Traditional UML Diagram of the Chain Of Command Design Pattern Objects

cofcdiagram.png

Introduction to the Example

The example code presents simplified examples of using the chain of command pattern concepts to perform validation on, and processing of a set of data. The techniques can then be applied to real-life, complex requirements. Presented here are the requirements for our three sample problems: (1) Validating login data (user name/password), (2) Validating bank transaction data, and (3) Processing a bank transaction (a deposit or withdrawal).

Requirements for Authorizing a Login

  1. The login authorization process should STOP when the first error is encountered, and not keep processing the rest of the requirements.
  2. An integer login id must not be less than or equal to zero.
  3. The integer login id must be found in the data store.

Requirements for Validating a Bank Account Transaction

  1. The transaction validation process should examine all data fields of a transaction, regardless of errors in other data fields.
  2. A transaction string 'account number' must be found in the data store.
  3. A transaction 'type' single character must be either a 'D' character for a deposit transaction, or a 'W' character for a withdrawal transaction.
  4. A transaction floating point 'amount' must be a positive value (i.e. greater than zero).

Requirements for Processing A Bank Transaction

  1. A transaction with a type 'D' and a positive amount will be processed as a deposit
  2. A transaction with a type 'W' and a positive amount will be processed as a withdrawal.

Variations on the Chain Of Command Theme Used for this Example

This implementation of the chain deviates from the classical Chain of Command Design pattern, and does not attempt to strictly adhere to the classic pattern description. Even though some may disagree, there are no rules about how a pattern should or should not be applied or changed. We are free to modify them to suit our requirements!

Pieces of the Puzzle in the Implementation of the Chain Mechanism

  • Each handler is an object that implements an IChainHandler interface.
  • Each handler can optionally throw a ChainHandlerException, which indicates some exception condition during processing.
  • Each handler returns a HandlerResult: either a HandlerResult.CHAIN_DATA_HANDLED, indicating the request was handled and no further processing required, or a HandlerResult.CHAIN_DATA_NOT_HANDLED, indicating the request was NOT handled, and the request should be passed to the next handler in the chain.
  • Instead of implementing a chain as a linked list, the actual chain is a List of handlers maintained by a ChainManager class.  The manager iterates over this list, invoking each handler's ProcessRequest() method.
  • To process a request, the ChainManager.ProcessRequest() method is called.
  • Processing the chain can include the following variations: (1) Pass request data to all handlers, regardless of the HandlerResult returned by each handler, (2) Stop when first handler indicates that it has handled the request successfully, (3) Stop when a handler throws an exception, or (4) Collect all exceptions into a list and pass back that list to the client for analysis.

Two variables passed into the ChainManager constructor, which are exposed as read only properties, affect the behavior of chain processing:

  • StopOnFirstException : Indicates whether the manager should stop the chain processing based on if an exception is thrown by the current handler being processed, or continue down the chain, allowing all handlers to process data regardless of any exceptions that have occurred.
  • ProcessEntireChain : If true, this flag indicates all handlers in the chain should process the request, regardless of the HandlerResult value returned by the individual handlers. This setting overrides the individual HandlerResult returned values.

Considerations of constructing chains

While attempting to create a flexible chain system, some confusion may occur over the handler return values as they relate the two behavior variables mentioned above.

The meaning of the handler return values, exception lists, are left up to the developer. The flexibility allows one to develop different kinds of chains with varying behaviors.

Some questions to consider when constructing the chain:

  • Should only one or all handlers process the clients request?
  • Will the handlers produce meaningful exception information to be used by the client? (i.e. the list of exceptions set by the manager's ChainManager.ProcessRequest() method)
  • Should chain processing stop on the first exception, or is it desired to accumulate a list of all exceptions for later analysis?

VS2008 Diagram

The following Visual Studio 2008 class diagram graphically shows all the items involved in the implementation of this example chain system:

ClassDiagram1.png

Using the Code: Constructing the Login Validation Chain

The login validation chain is constructed in the method, MainForm.ConstructLoginValidationChain():

C#
// Variables to pass into constructor 
bool stopOnFirstHandlerException; 
bool processEntireChain; 

// Create the login authorization chain: 
stopOnFirstHandlerException = true; 
processEntireChain = false; 
loginAuthorizationChain = new ChainManager < LoginData >(stopOnFirstHandlerException,
    processEntireChain); 

// Build the chain 
loginAuthorizationChain.AppendHandlerToChain(new LoginIdZeroOrNegativeValidationHandler()); 
loginAuthorizationChain.AppendHandlerToChain(new LoginIdValidationHandler()); 
loginAuthorizationChain.AppendHandlerToChain(new LoginPasswordValidationHandler());

The client request for chain processing, and the analysis of the returned exceptions are performed in MainForm.btnValidateLoginData_Click()

C#
// Get the login data from the screen:            
LoginData loginData = CreateLoginDataFromScreen();

// List of exception(s) encountered during the login validation process.
List < ChainHandlerException > validationExceptions;            

// Submit request for the login validation chain, passing in the login data as argument.
chainProcessingResult = loginAuthorizationChain.ProcessRequest(loginData,
    out validationExceptions);
            
if (validationExceptions.Count > 0)
   // If exceptions are encountered, then at least one handler found a problem
   // with the validation
else
   // No exceptions were returned, indicating all handlers validated their portion
   // of the data successfully

Note that ChainManager accepts one generic argument, LoginData. This is the object type that is passed into the ChainManager's ProcessRequest() method, and also the type that is accepted by all of the chain handlers in this chain.  Generics were used to provide type safety for the chain manager and the managed handlers.

Diagram of Login Validation Chain And Client Request

loginchain.png

See the corresponding method MainForm.ConstructAccountDataValidationChain() and btnValidateTransaction_Click() for the construction and usage of the account transaction validation chain.

Note the example of processing transaction data uses the more traditional chain of command mechanism, with only one handler processing the request. See MainForm.ConstructTransactionProcessingChain() for the construction of the chain and required behavior, and MainForm.btnProcessTransaction_Click()

Points of Interest - Generics For Type Safety

Generics are used in the ChainManager class and  the IChainHandler interface to provide type safety of the request data.  Below are excerpts showing how the single generic argument is used:

Chain management class declaration

public class ChainManager < HANDLER_REQUEST_TYPE >       
{ 
    // Declaration for the list of handlers
    private List< IChainHandler < HANDLER_REQUEST_TYPE > > handlerChain = 
        new List < IChainHandler < HANDLER_REQUEST_TYPE > >();	

    // Declaration of the Manager's request method:
    public HandlerResult ProcessRequest(HANDLER_REQUEST_TYPE requestData,
        out List < ChainHandlerException > chainHandlerExceptionList )
    { ... }
}

Chain Handler Interface Declaration

public interface IChainHandler < HANDLER_REQUEST_TYPE > 
{  
    HandlerResult ProcessRequest(HANDLER_REQUEST_TYPE requestData); 
}

Example declaration of a chain handler

C#
public class LoginIdValidationHandler : IChainHandler < LoginData > 
{
   public HandlerResult ProcessRequest(LoginData requestData) { ...}
}

Conclusion

The Chain of Command design pattern uses relatively simple, loosley coupled, isolated programming units (i.e. chain handlers) linked together to form a chain. The client makes one request to the chain for processing, and has no knowledge of internal chain structure. Chains can be dynamically allocated and modified.

Use this design pattern to break down and solve complicated data processing tasks, which will increase maintainability and flexibility, while reducing the complexity of software solutions.

History

August 10, 2009    Initial creation of example project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Senior Software Developer with background history in developing software for both Unix and Windows based systems using a variety of languages and technologies, including: C#, C++, C, and Java.

Comments and Discussions

 
GeneralFabricated pattern name Pin
Yevhen Bobrov8-Mar-11 9:58
Yevhen Bobrov8-Mar-11 9:58 
GeneralRe: Fabricated pattern name Pin
Curt C9-Mar-11 5:17
Curt C9-Mar-11 5:17 
GeneralAsync chains Pin
Adriaan Davel7-Mar-11 19:03
Adriaan Davel7-Mar-11 19:03 
GeneralRe: Async chains Pin
Curt C8-Mar-11 3:18
Curt C8-Mar-11 3:18 
GeneralMy vote of 5 Pin
Paul C Smith3-Mar-11 16:41
Paul C Smith3-Mar-11 16:41 
GeneralRe: My vote of 5 Pin
Curt C8-Mar-11 3:18
Curt C8-Mar-11 3:18 
RantJust some .NET related notes Pin
kornman003-Mar-11 10:35
kornman003-Mar-11 10:35 
GeneralRe: Just some .NET related notes Pin
Ian Good3-Mar-11 14:48
Ian Good3-Mar-11 14:48 
GeneralRe: Just some .NET related notes Pin
Curt C8-Mar-11 3:24
Curt C8-Mar-11 3:24 
Greetings - Thanks for the standards suggestions - Yes I agree with you - these are good recommendations. I have a habit, especially when writing code that others read, to be a little over verbose in my naming conventions, just in an attempt to clarify what the code is doing. Hopefully one would be able to read a line of code like a sentence and would be self explanatory - but this comes at the expense of being to wordy Frown | :(

I'm working on the next article, and I'll make sure to pay close attention to the coding standards in the next project.

Thank you for you great explanations and examples you provided on how to improve the coding style.

Best Wishes and Happy Coding.

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

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