Patterns. Chain of Responsibility pattern. An example of implementation in C#

Chain of Responsibility pattern. An example of implementation in C# for a given class structure

This topic is intended to study the features of the implementation of the Chain of Responsibility pattern using an example of some simplified structure of classes. Before studying the topic, it is recommended that you familiarize yourself with the features of the pattern, which are presented in the topic:


Contents


Search other resources:

1. Task

Develop an application using the Chain of Responsibility pattern based on the given conditions. The program must implement the execution of a request from the client, which, depending on the entered value of the operation code (+, , *, /, %), must receive the corresponding result of the operation on two numbers. The following operations are considered:

  • addition of two numbers, operation code + (plus);
  • subtraction of two numbers, operation code (minus);
  • multiplication of two numbers, operation code *;
  • division of two numbers. Here you need to implement integer division (operation code %) and ordinary division taking into account the fractional part of the result (operation code /).

 

2. Solution
2.1. Entering the processing method

First of all, a method is introduced into each class that will process the request from the client. In our case, this method is called OperationRequest(). The method will receive two parameters and perform one of the operations in accordance with the condition of the task.

2.2. Development of the structure of classes that correspond to the Chain of Responsibility pattern

Taking into account the general structure of the Chain of Responsibility pattern and the input of the OperationRequest() method, the class structure shown in Figure 1 is developed. Further steps of this topic contain a description of the work of the developed classes.

Pattern "Chain of Responsibility". Implementation in C#. Class structure

Figure 1. The structure of the classes corresponding to the condition of the problem

 

2.3. Class Operation

The Operation class is the base class in the class hierarchy. In this class, all the necessary tools are created to implement the Chain of Responsibility pattern. The following elements are declared in the class:

  • internal field successor, which is a reference to the successor. This reference is processed in each Add, Sub, Mult, DivInt, DivFloat handler class. In the Mult class, this reference is set to null because this class is the last in the chain of handlers;
  • internal numOp field that defines the operation. This field is of type char and contains the operation designation (+, , *, /, %);
  • a constructor that takes two parameters: a reference to a successor and operation type;
  • virtual method OperationRequest() that directly handles the request. This method is overridden by methods of the same name in inherited classes;
  • the DoRequest() method, which, based on the numOp operation code, determines whether to process the request. If the numOp value is ‘0’, then the request is not processed;
  • the SetHandler() method – implements the installation of the handler (successor) and processing operations assigned to it (numOp).

After entering the class, the program listing is as follows

using System;

namespace ConsoleApp20
{
  // A class that is a common interface for handler classes
  class Operation
  {
    // 1. Internal variables
    // 1.1. Reference to successor
    private Operation successor;

    // 1.2. The identifier of the handler that handles the request
    private char numOp;

    // 2. Constructors
    public Operation(Operation _successor = null, char _numOp = '0')
    {
      successor = _successor;
      numOp = _numOp;
    }

    // 3. A method that is chain-processed
    public virtual double OperationRequest(int num1, int num2)
    {
      if (successor != null)
        return successor.OperationRequest(num1, num2);
      else
        return 0.0;
    }

    // 4. Method that determines if the request should be processed
    public bool DoRequest()
    {
      return numOp != '0';
    }

    // 5. The method that sets the handler and the ability to handle
    public void SetOperation(Operation _successor, char _numOp)
    {
      successor = _successor;
      numOp = _numOp;
    }
  }
}

In the next steps, other classes will be added to the namespace.

 

2.4. Handler class Add

The Add handler class implements the operation of adding two numbers. This class inherits from the Operation base class. The addition is implemented directly in the overridden OperationRequest() method.

The text of the class is as follows

using System;

namespace ConsoleApp20
{

  ...

  // A handler class that implements the operation of adding numbers.
  class Add : Operation
  {
    // Constructor, calls the constructor of the base class
    public Add(Operation _operation, char _numOp)
        : base(_operation, _numOp)
    {
    }

    // A method that adds two numbers
    public override double OperationRequest(int num1, int num2)
    {
      if (base.DoRequest())   // If the addition operation is allowed,
        return num1 + num2; // then return the sum of numbers
      else // otherwise, pass processing to the next method in the chain
        return base.OperationRequest(num1, num2);
    }
  }
}

 

2.5. Handle class Sub

The Sub class, which implements the subtraction of two numbers, works in the same way as the Add class (see section 2.4).

Below is the listing of the class

...

// A handler class that implements the operation of subtracting numbers.
class Sub : Operation
{
  // Constructor, calls the constructor of the base class
  public Sub(Operation _operation, char _numOp)
      : base(_operation, _numOp)
  {
  }

  // A method that implements subtraction of numbers
  public override double OperationRequest(int num1, int num2)
  {
    if (base.DoRequest())   // If subtraction is allowed,
      return num1 - num2; // then return the difference of numbers
    else // otherwise pass processing further along the chain
      return base.OperationRequest(num1, num2);
  }
}

...

 

2.6. Intermediate class Div

The Div class is the base class for the DivInt and DivFloat handler classes that implement division of two numbers. In turn, to ensure the correct organization of the Chain of Responsibility pattern, the Div class inherits from the Operation class.

The constructor for the Div class redirects the instantiation of one of the inherited classes to the constructor for the Operation class. Other operations (methods) for processing numbers can be added to the Div class.

Listing of class Div is shown below

...

// Base class for the DivInt and DivFloat classes.
// These classes implement integer and non-integer division operations.
class Div : Operation
{
  // Class constructor
  public Div(Operation _parent, char _numOp) : base(_parent, _numOp)
  {
  }
}

...

 

2.7. DivInt and DivFloat handler classes

The DivInt and DivFloat handler classes inherit from the Div class. When instantiated, the constructors of these classes call the constructor of the base class Div. The OperationMethod() method in these handler classes works in the same way as in the Add and Sub classes.

The listing of the classes looks like this

...

// Handler class that inherits from the Div class.
// The class implements the integer division operation,
// in which the fractional part of the result is discarded.
class DivInt : Div
{
  // Constructor - calls the constructor of the base class Div
  public DivInt(Operation _parent, char _numOp) : base(_parent, _numOp)
  { }

  // Specific processing method
  public override double OperationRequest(int num1, int num2)
  {
    if (base.DoRequest())         // if division operation is allowed,
      return (int)(num1 / num2); // then return integer division
    else // otherwise go to the next method in the chain
      return (int)base.OperationRequest(num1, num2);
  }
}

// Handler class that inherits from the Div class.
// The class implements the normal division operation,
// which returns a floating point number.
class DivFloat : Div
{
  // Constructor - calls the constructor of the base class Div
  public DivFloat(Operation _parent, char _numOp) : base(_parent, _numOp)
  { }

  // Specific processing method
  public override double OperationRequest(int num1, int num2)
  {
    if (base.DoRequest())     // if division operation is allowed,
      return (double)num1 / num2; // then return normal division
    else // otherwise go to the next method in the chain
      return base.OperationRequest(num1, num2);
  }
}

...

 

2.8. Handler class Mult. The last handler in the chain

The last in the chain of classes, processing is performed by the Mult class, which is inherited from the base class Operation. The OperationRequest() method and the constructor of this class have their own peculiarities. The constructor does not need to specify an inheritor, since the Mult class is the last in the chain of handlers. Also, the OperationRequest() method displays an appropriate message if the request from the client could not be processed.

The listing of the Mult class is as follows

...

// Handler class that implements the operation of multiplying numbers.
// This is the last handler in the chain of methods,
// therefore, the OperationReques() method is not similar to the methods of the same name in other handlers.
class Mult : Operation
{
  // Constructor, calls the base class constructor.
  // Since this handler is the last in the chain of methods,
  // then he does not need to pass a reference to the previous handler.
  public Mult(char _numOp)
      : base(null, _numOp)
  {
  }

  // Method that multiplies two numbers
  public override double OperationRequest(int num1, int num2)
  {
    if (base.DoRequest())   // If the multiplication operation is enabled,
      return num1 * num2; // then return the product of numbers
    else
      // otherwise, display a message stating that there is no corresponding handler in the chain
    {
      Console.WriteLine("There is no corresponding handler in the chain.");
      return 0.0;
    }
  }
}

...

 

2.9. The client’s Program class. Function main()

The client in this example is the main() function from the Program class.

In the main() function, the following actions are performed:

  • a chain of handlers is created based on the handler classes described above;
  • a client object is created, pointing to the chain of handler objects;
  • how to install and call the appropriate handler based on simplified dispatch code is demonstrated.

The text of the Program class and the main() function is as follows

...

class Program
{
  static void Main(string[] args)
  {
    // Client code
    // 1. Create a chain of handler classes
    //   The chain is created in order from last handler to first
    // 1.1. The last handler in the chain is created first.
    Mult multOp = new Mult('*');

    // 1.2. Add DivInt handler
    // The handler gets a reference to the previously created multOp handler
    DivInt divIntOp = new DivInt(multOp, '%'); // % - points to integer division

    // 1.3. Add DivFloat handler
    DivFloat divFloatOp = new DivFloat(divIntOp, '/');

    // 1.4. Add next handler Sub.
    // The handler gets a reference to the previously created handler
    Sub subOp = new Sub(divFloatOp, '-');

    // 1.5. By the same principle, add the Add handler
    Add addOp = new Add(subOp, '+');

    // 2. Create a client.
    // Client points to a chain of handler objects
    // client -> addOp -> subOp -> divFloatOp -> divIntOp -> multOp
    Operation client = new Operation(addOp);

    // 3. Set the operation to be performed
    Console.Write("Operation = ");
    char op = Convert.ToChar(Console.ReadLine());

    // 4. Set two numbers that will be processed
    int num1, num2;
    Console.Write("num1 = ");
    num1 = Convert.ToInt32(Console.ReadLine());
    Console.Write("num2 = ");
    num2 = Convert.ToInt32(Console.ReadLine());

    // 4. Depending on op, implement dispatching
    double res; // The result of the calculation of some operation

    switch(op)
    {
      case '+':
        // Install addOp handler for operation '+'
        client.SetOperation(addOp, '+');

        // Call the processing method and display the result
        res = client.OperationRequest(num1, num2); // num1+num2
        Console.WriteLine("res = {0:f2}", res);
        break;
      case '-':
        // Install subOp handler for operation '-'
        client.SetOperation(subOp, '-');

        // Call the processing method and display the result
        res = client.OperationRequest(num1, num2); // num1-num2
        Console.WriteLine("res = {0:f2}", res);
        break;
      case '%':
        // Set divIntOp handler for operation '%'
        client.SetOperation(divIntOp, '%');

        // Call the processing method and display the result
        res = client.OperationRequest(num1, num2); // num1%num2 
        Console.WriteLine("res = {0:f2}", res);
        break;
      case '/':
        // Set divFloatOp handler for '/' operation
        client.SetOperation(divFloatOp, '/');

        // Call the processing method and display the result
        res = client.OperationRequest(num1, num2); // num1 / num2
        Console.WriteLine("res = {0:f2}", res);
        break;
      case '*':
        // Set multOp handler for operation '*'
        client.SetOperation(multOp, '*');

        // Call the processing method and display the result
        res = client.OperationRequest(num1, num2);
        Console.WriteLine("res = {0:f2}", res);
        break;
      default:
        Console.WriteLine("Incorrect operation");
        break;
    }
  }
}

 

2.10. Abbreviated program code

Using the key combinations Ctrl+C, Ctrl+V, you can compose the text of the demo program based on the texts of the classes from paragraphs 2.3-2.9. The program is implemented as an application of the Console Application type.

The abbreviated text of the program is as follows

using System;

namespace ConsoleApp20
{
  // A class that is a common interface for handler classes
  class Operation
  {
    ...
  }

  // A handler class that implements the addition operation of numbers.
  class Add : Operation
  {
    ...
  }

  // A handler class that implements the operation of subtracting numbers.
  class Sub : Operation
  {
    ...
  }

  // The base class for the DivInt and DivFloat classes.
  // These classes implement integer and non-integer division operations.
  class Div : Operation
  {
    ...
  }

  // Handler class that inherits from the Div class.
  class DivInt : Div
  {
    ...
  }

  // Handler class that inherits from the Div class.
  class DivFloat : Div
  {
    ...
  }

  // Handler class that implements the operation of multiplying numbers.
  class Mult : Operation
  {
    ...
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Client code (Client)
      ...
    }
  }
}

 


Related topics