Chain of Responsibility Design Pattern in R

1. Definition

The Chain of Responsibility Design Pattern allows multiple objects to process a request, either handling it or passing it along the chain until an object handles it. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.

2. Problem Statement

Imagine you're designing a system to validate form input. Different validators need to check for different criteria, like length, characters used, and format. How do you efficiently pass the input through all these validators without tightly coupling them?

3. Solution

Use the Chain of Responsibility pattern to create a chain of validator objects. Each validator checks for specific criteria and, if valid, passes the input to the next validator in the chain.

4. Real-World Use Cases

1. Event handling systems where an event can be processed by multiple handlers or passed up a widget hierarchy.

2. Middleware in web frameworks where requests pass through a series of processing stages.

3. Input validation, as illustrated in our example.

5. Implementation Steps

1. Define a handler interface that dictates methods for setting the next handler in the chain and for processing the request.

2. Create concrete handlers implementing this interface.

3. Clients create a chain by linking handlers, and then send requests into this chain.

6. Implementation in R Programming

# Step 1: Handler Interface
Handler <- setRefClass("Handler", methods = list(
  set_next = function(next_handler) {},
  handle = function(request) {}
))
# Step 2: Concrete Handlers
LengthValidator <- setRefClass("LengthValidator", contains = "Handler",
  fields = list(next_handler = "Handler"),
  methods = list(
    set_next = function(next_handler) {
      .self$next_handler <- next_handler
    },
    handle = function(request) {
      if (nchar(request) > 5) {
        return("Length is too long.")
      } else if (!is.null(.self$next_handler)) {
        return(.self$next_handler$handle(request))
      }
    }
  )
)
CharacterValidator <- setRefClass("CharacterValidator", contains = "Handler",
  fields = list(next_handler = "Handler"),
  methods = list(
    set_next = function(next_handler) {
      .self$next_handler <- next_handler
    },
    handle = function(request) {
      if (grepl("[^a-zA-Z0-9]", request)) {
        return("Invalid characters found.")
      } else if (!is.null(.self$next_handler)) {
        return(.self$next_handler$handle(request))
      }
    }
  )
)
# Client code
length_validator <- LengthValidator$new()
character_validator <- CharacterValidator$new()
length_validator$set_next(character_validator)
input_string <- "abc1@"
output <- length_validator$handle(input_string)

Output:

Invalid characters found.

Explanation:

1. We first define a Handler interface, which will be the blueprint for our concrete handlers.

2. Next, we create two concrete handlers: LengthValidator and CharacterValidator.

3. In the client code, we chain the validators together. In this case, LengthValidator is followed by CharacterValidator.

4. When handling the input_string, it first goes through the LengthValidator. If it passes validation, it moves to the CharacterValidator.

5. In our example, the input_string has invalid characters, so the CharacterValidator provides the output.

7. When to use?

Use the Chain of Responsibility Pattern when:

1. Multiple objects may handle a request, and the handler isn't predetermined. It's determined at runtime among the objects in the chain.

2. You want to reduce the coupling between a request sender and its receivers by allowing more than one object to handle the request.

3. The set of objects that can handle a request should be specified dynamically.

By creating a chain of responsibility, the system gains flexibility and separation of concerns, as each handler is independent and can be added or removed from the chain dynamically.


Comments