NOTE

Chain of Responsibility

The chain-of-responsibility pattern links handlers that can process the same type of request, with Java, Go, and filter-chain examples.

Software Architecture & EngineeringCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. Definition

Connect objects that can handle the same type of request into a chain, such as servlet filters.

2. UML

PlantUML 图表

3. Java

3.1. Client

public class Client
{
    public static void main(String[] args)
    {
        LeaveRequest request = new LeaveRequest(2);

        Leader boss = new Boss(null);
        Leader manager = new Manager(boss);

        manager.handlerRequest(request);
    }
}

3.2. Processing Chain

public abstract class Leader
{
    protected Leader nextLeader;

    public Leader(Leader nextLeader)
    {
        this.nextLeader = nextLeader;
    }

    public abstract void handlerRequest(LeaveRequest request);
}
public class Boss extends Leader
{
    public Boss(Leader nextLeader)
    {
        super(nextLeader);
    }

    @Override
    public void handlerRequest(LeaveRequest request)
    {
        if (request.getLeaverDays() < 30)
            System.out.println("Boss approves");
        else
        {
            if (this.nextLeader != null)
                this.nextLeader.handlerRequest(request);
            System.out.println("Rejected");
        }
    }
}
public class Manager extends Leader
{
    public Manager(Leader nextLeader)
    {
        super(nextLeader);
    }

    @Override
    public void handlerRequest(LeaveRequest request)
    {
        if (request.getLeaverDays() < 3)
            System.out.println("Manager approves");
        else
        {
            if (this.nextLeader != null)
                this.nextLeader.handlerRequest(request);
        }
    }
}

3.3. Request

public class LeaveRequest
{
    private int leaverDays;

    public LeaveRequest(int leaverDays)
    {
        this.leaverDays = leaverDays;
    }

    public int getLeaverDays()
    {
        return leaverDays;
    }

    public void setLeaverDays(int leaverDays)
    {
        this.leaverDays = leaverDays;
    }
}

4. Golang

4.1. Client

func main() {
	boss := 责任链.NewBoss()
	deptLeader := 责任链.NewDeptLeader()
	teamLeader := 责任链.NewTeamLeader()
	teamLeader.SetSuccessor(deptLeader)
	deptLeader.SetSuccessor(boss)

	request := 责任链.NewVacationRequest(5)
	teamLeader.HandleVocationRequest(request)
}

4.2. Processing Chain

/* Manager interface */
type IManager interface {
	SetSuccessor(IManager)
	HandleVocationRequest(*VacationRequest) bool
}

/* Base manager implementation */
type BaseManager struct {
	Successor IManager
}

func (m *BaseManager) SetSuccessor(manager IManager) {
	m.Successor = manager
}

func (m *BaseManager) HandleVocationRequest(request *VacationRequest) bool {
	return false
}

type TeamLeader struct {
	BaseManager
}

func NewTeamLeader() *TeamLeader {
	return &TeamLeader{}
}

func (t *TeamLeader) HandleVocationRequest(request *VacationRequest) bool {
	if request.Day < 3 {
		fmt.Printf("TeamLeader approves leave request [%d]
", request.Day)
		return true
	} else if t.Successor != nil {
		fmt.Printf("TeamLeader is not authorized to approve leave request [%d], pass it to [%s]
", request.Day, "the next approver")
		t.Successor.HandleVocationRequest(request)
	}

	return false
}

type DeptLeader struct {
	BaseManager
}

func NewDeptLeader() *DeptLeader {
	return &DeptLeader{}
}

func (t *DeptLeader) HandleVocationRequest(request *VacationRequest) bool {
	if request.Day < 10 {
		fmt.Printf("DeptLeader approves leave request [%d]
", request.Day)
		return true
	} else if t.Successor != nil {
		fmt.Printf("DeptLeader is not authorized to approve leave request [%d], pass it to [%s]
", request.Day, "the next approver")
		t.Successor.HandleVocationRequest(request)
	}

	return false
}

type Boss struct {
	BaseManager
}

func NewBoss() *Boss {
	return &Boss{}
}

func (t *Boss) HandleVocationRequest(request *VacationRequest) bool {
	fmt.Printf("Boss approves leave request [%d]
", request.Day)
	return true
}

4.3. Request

/* Leave request */
type VacationRequest struct {
	Day uint32
}

func NewVacationRequest(day uint32) *VacationRequest {
	return &VacationRequest{Day: day}
}

5. Example

5.1. Filter Chain

type Config struct {
	key   string
	value string
}

// Filter function
type filterFunc func(config *Config) bool

// Filter chain
type FilterChain struct {
	filterChains []filterFunc
}

// Add a filter
func (f *FilterChain) addFilter(filter filterFunc) {
	f.filterChains = append(f.filterChains, filter)
}

// Execute the filters
func (f *FilterChain) filter(config *Config) bool {
	for _, filter := range f.filterChains {
		if filter(config) {
			return true
		}
	}

	return false
}

// Key filter
func keyFilter(config *Config) bool {
	if config.key == "name" {
		return true
	}
	return false
}

// Value filter
func valueFilter(config *Config) bool {
	if config.value == "[REDACTED]" {
		return true
	}
	return false
}

var defaultFilterChain = &FilterChain{}

func init() {
	defaultFilterChain.addFilter(keyFilter)
	defaultFilterChain.addFilter(valueFilter)
}

func main() {
	fmt.Println(defaultFilterChain.filter(&Config{}) == false)
	fmt.Println(defaultFilterChain.filter(&Config{key: "key"}) == false)
	fmt.Println(defaultFilterChain.filter(&Config{key: "name"}) == true)
	fmt.Println(defaultFilterChain.filter(&Config{value: "[REDACTED]"}) == true)
	fmt.Println(defaultFilterChain.filter(&Config{value: "example"}) == false)
}

6. References

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub