DEV Community

Furkan Aksoy
Furkan Aksoy

Posted on

Chain of Responsibility Pattern in Go

The Chain of Responsibility Pattern is a behavioral design pattern that allows an object to pass a request along a chain of handlers. The chain of handlers can either handle the request or pass it on to the next handler in the chain. The request is passed along the chain until it is handled or until it reaches the end of the chain.

In this article, we will explore the Chain of Responsibility Pattern in Go. We will create a simple example to demonstrate how the pattern works and how it can be implemented in Go.

The Chain of Responsibility Pattern is commonly used in scenarios where there are multiple objects that can handle a request, and the sender of the request does not know which object will handle the request. Examples of such scenarios include event handling, logging, and error handling.

Example

Let's create a simple example to demonstrate the Chain of Responsibility Pattern in Go. In this example, chain will be responsible to validate the request, printing the error logs and handling the request itself.

package service

import "fmt"

type Service interface {
    HelloWorld(name string) (string, error)
}

type service struct {}

func (s service) HelloWorld(name string) (string, error) {
    return fmt.Sprintf("Hello World from %s", name)
}

type validator struct {
    next Service
}

func (v validator) HelloWorld(name string) (string, error) {
    if len(name) <= 3 {
        return "", fmt.Errorf("name length must be greater than 3")
    }

    return v.next(name)
}

type logger struct {
    next Service
}

func (l logger) HelloWorld(name string) (string, error) {
    res, err := l.next(name)

    if err != nil {
        fmt.Println("error:", err)
        return res, err
    }

    fmt.Println("HelloWorld method executed successfuly")
    return res, err
}

func New() Service {
    return logger{
        next: validator {
            next: service{}
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
package main

import (
    "fmt"
    "service"
)

func main() {
    s := service.New()

    res, err := s.HelloWorld("john")
    fmt.Println(res, err) // Hello World from john

    res, err := s.HelloWorld("joh")
    fmt.Println(res, err) // name length must be greater than 3 
}
Enter fullscreen mode Exit fullscreen mode

In this example, we have created a simple service that implements the Service interface. The Service interface defines a method HelloWorld that takes a name as an argument and returns a greeting message.

We have also created two additional structs validator and logger that implement the Service interface. The validator struct validates the length of the name and returns an error if the length is less than or equal to 3. The logger struct prints the error logs and the success message when the HelloWorld method is executed successfully.

The New function creates a chain of handlers by chaining the validator and logger structs with the service struct. The service struct is the last handler in the chain and handles the request if it passes the validation.

What is next field in this example?

The next field in the validator and logger structs is used to chain the handlers. The next field holds the next handler in the chain. When the HelloWorld method is called on a handler, it calls the HelloWorld method on the next handler in the chain using the next field.

Conclusion

In this article, we explored the Chain of Responsibility Pattern in Go. We created a simple example to demonstrate how the pattern works and how it can be implemented in Go. I hope this article helps you understand the Chain of Responsibility Pattern and how it can be used in your projects. Happy coding!

Top comments (0)