Chain Of Responsibility with Scala Partial Function

Saurabh Kishore Tiwari
The Thought Mill
4 min readMay 22, 2023

--

What are Partial Functions?

In Mathematics, a partial function is a function that is not defined for all possible input values. It is a function that is only defined for a subset of the input domain. When you call a partial function with an input value that is not within its defined subset, it will throw an exception.

For example, consider the function

y = tan θ

y is defined for all values except odd multiples of 90◦.

Let’s see one more example. The classic division operator.

y = 1/x

y is defined for all values of x except 0.

I think you get what I’m trying to say.

Chain of responsibility

The Chain of Responsibility pattern is a behavioral design pattern that allows an object to pass a request along a chain of potential handlers until the request is handled or reaches the end of the chain. It decouples the sender of a request from its receiver, giving multiple objects an opportunity to handle the request.

In this pattern, there is a chain of handler objects, typically implemented as a linked list. Each handler has a reference to the next handler in the chain. When a request is made, it is first received by the first handler in the chain. If the handler can handle the request, it does so and the request processing stops. If the handler cannot handle the request, it passes the request to the next handler in the chain. This process continues until the request is handled or the end of the chain is reached.

The key participants in the Chain of Responsibility pattern are:

  1. Handler: It defines an interface for handling requests and optionally implements the successor link to the next handler in the chain.
  2. ConcreteHandler: It implements the Handler interface and handles requests it is responsible for. It can also forward the request to its successor.
  3. Client: It initiates the request and starts the chain traversal.

The Chain of Responsibility pattern promotes loose coupling between senders and receivers of requests, allowing multiple objects to have a chance to handle a request without explicitly knowing which object will handle it. It provides flexibility in dynamically configuring the chain and adding or removing handlers at runtime. Additionally, it simplifies the sender’s code by removing the need to specify the receiver explicitly.

Code

In this post, we’ll have each handler as a Partial Function and we’ll make a list of these handlers and pass each request.

object ChainOfResponsibility extends App {
private val multipleOfThree: PartialFunction[Int, String] = {
case i if i % 3 == 0 => s"$i is divisible by 3"
}
private val multipleOfFive: PartialFunction[Int, String] = {
case i if i % 5 == 0 => s"$i is divisible by 5"
}
private val multipleOfBoth: PartialFunction[Int, String] = {
case i if i % 3 == 0 && i % 5 == 0 => s"$i is divisible by 3 & 5"
}
private val default: PartialFunction[Int, String] = {
case i => s"$i is not divisible by 3 & 5"
}

private val handlers = List(multipleOfBoth, multipleOfThree, multipleOfFive, default)
.reduce(_ orElse _)

private val requests = (1 to 100)

requests foreach (i => println(handlers(i)))
}

Chain Of Responsibility is implemented using orElse. It allows us to chain the method calls

Output

1 is not divisible by 3 & 5
2 is not divisible by 3 & 5
3 is divisible by 3
4 is not divisible by 3 & 5
5 is divisible by 5
6 is divisible by 3
7 is not divisible by 3 & 5
8 is not divisible by 3 & 5
9 is divisible by 3
10 is divisible by 5
11 is not divisible by 3 & 5
12 is divisible by 3
13 is not divisible by 3 & 5
14 is not divisible by 3 & 5
15 is divisible by 3 & 5
16 is not divisible by 3 & 5
17 is not divisible by 3 & 5
18 is divisible by 3
19 is not divisible by 3 & 5
20 is divisible by 5
.
.
.

Usage and Similar Pattern

The Chain of Responsibility pattern allows you to add or remove handlers dynamically and provides flexibility in handling requests based on runtime conditions. It also promotes loose coupling between the sender and receiver of a request.

This pattern is commonly used in scenarios where there are multiple objects that can handle a request, and the specific handler is determined dynamically at runtime based on the characteristics of the request. Examples include

  1. Event handling systems
  2. Logging frameworks
  3. Request processing pipelines

Similar patterns to the Chain of Responsibility include the Command pattern and the Observer pattern. The Command pattern encapsulates a request as an object, allowing you to parameterize clients with different requests and support undo operations. The Observer pattern establishes a one-to-many relationship between objects, so that when one object changes its state, all dependent objects are notified and updated automatically.

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job

--

--