BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Design Patterns
.NET 1.1+

Object Pipelines

A pipeline is a chain of connected steps that process information. In object pipelines, each step receives an object and performs an action using it before passing an object to the next step. This repeats until every step is complete.

Example Pipeline

In this section we'll create an example pipeline that performs processing of sales orders taken via a web site. The sales order will be a very simplified example, allowing a single product to be purchased. The class for order objects is shown below. It includes a code for the product that was ordered, a flag that specifies whether the product is software that can be downloaded or a physical product for which stock must be allocated in a sales order processing (SOP) system and the email address of the customer. The class also includes an order number property, which will be set by a pipeline step.

public class SalesOrder
{
    public string ProductCode { get; set; }
    public bool IsSoftwareProduct { get; set; }
    public string CustomerEmail { get; set; }
    public string OrderNumber { get; set; }
}

The base class for pipeline steps is slightly modified from the template version shown earlier. It includes a protected method named, "GetOrderOrNextStepResult". This will be called by each step after processing the input to either return the order or pass it to the next step for further processing.

public abstract class OrderPipelineStep
{
    protected OrderPipelineStep _nextStep;

    public void SetNextStep(OrderPipelineStep nextStep)
    {
        _nextStep = nextStep;
    }

    public abstract SalesOrder HandleOrder(SalesOrder order);

    protected SalesOrder GetOrderOrNextStepResult(SalesOrder order)
    {
        return _nextStep == null ? order : _nextStep.HandleOrder(order);
    }
}

Finally we can implement the steps that we want to be available for use in sales order pipelines. The code below defines four steps. Each simulates a real action by outputting a message to the console.

  • StoreOrderStep. This simulates storing the sales order details in the SOP system. It also generates a new order number and applies it to the order. The method of generating the order number is poor for a real system but will suffice for our example. This is the only step that modifies the sales order. All of the other steps perform actions using the order data but do not change its values.
  • SendInvoiceStep. This simulates a class that sends an invoice to the customer by email.
  • SendSoftwareProductEmailStep. This step is used to send an email to the customer with a download link for the software they purchased. If the sales order is for a physical product, the step does nothing.
  • AllocateStockStep. This step allocates product in the stock part of the SOP system. For software, no stock is held so this step performs no function.

You can probably think of further steps that may be required. A key part of the pipeline pattern described here is that new steps can be added easily and incorporated in pipelines as new requirements arise.

public class StoreOrderStep : OrderPipelineStep
{
    public override SalesOrder HandleOrder(SalesOrder order)
    {
        order.OrderNumber = DateTime.Now.ToString("yyyyMMddHHmmss");
        Console.WriteLine("Order #{0} stored in SOP system", order.OrderNumber);
        return GetOrderOrNextStepResult(order);
    }
}

public class SendInvoiceStep : OrderPipelineStep
{
    public override SalesOrder HandleOrder(SalesOrder order)
    {
        Console.WriteLine("Invoice sent to {0}", order.CustomerEmail);
        return GetOrderOrNextStepResult(order);
    }
}

public class SendSoftwareProductEmailStep : OrderPipelineStep
{
    public override SalesOrder HandleOrder(SalesOrder order)
    {
        if (order.IsSoftwareProduct)
        {
            Console.WriteLine("Download link sent to {0}", order.CustomerEmail);
        }
        return GetOrderOrNextStepResult(order);
    }
}

public class AllocateStockStep : OrderPipelineStep
{
    public override SalesOrder HandleOrder(SalesOrder order)
    {
        if (!order.IsSoftwareProduct)
        {
            Console.WriteLine("Product {0} allocated in SOP system", order.ProductCode);
        }
        return GetOrderOrNextStepResult(order);
    }
}

Testing the Pipeline

We can now create a pipeline using the step classes and use it to process a sample order. The code below creates a pipeline containing all four steps in the order that they were defined. It then passes an order for a physical product to the pipeline. Try executing the code and viewing the output. You should also try running the program with a software product and try modifying the combination of steps in the pipeline.

OrderPipelineStep storeOrder = new StoreOrderStep();
OrderPipelineStep sendInvoice = new SendInvoiceStep();
OrderPipelineStep sendSoftware = new SendSoftwareProductEmailStep();
OrderPipelineStep allocateStock = new AllocateStockStep();
storeOrder.SetNextStep(sendInvoice);
sendInvoice.SetNextStep(sendSoftware);
sendSoftware.SetNextStep(allocateStock);

SalesOrder order = new SalesOrder();
order.ProductCode = "ABC123";
order.IsSoftwareProduct = false;
order.CustomerEmail = "bob@abc!.com";

storeOrder.HandleOrder(order);

/* OUTPUT

Order #20120524201843 stored in SOP system
Invoice sent to bob@abc!.com
Product ABC123 allocated in SOP system

*/
24 May 2012