【设计模式】15、chain of responsibility 责任链模式

发布于:2024-05-06 ⋅ 阅读:(33) ⋅ 点赞:(0)

十五、chain of responsibility 责任链

https://refactoringguru.cn/design-patterns/chain-of-responsibility

如果需按顺序, 执行一系列步骤, 可用责任链.

链条上的每个步骤, 都持有下一个步骤的引用, 当自身步骤执行完毕后, 执行下一个步骤.

在运行时, client 也可以主动编辑 链的顺序.

核心是, 各步骤都实现相同的接口.

15.1 oa 审批系统

公司有流程申请系统, 员工的申请, 需要逐层审批. 是典型的链式.

基层员工, 先 HR 审批, 再 TL 审批, 再 BOSS 审批

├── oa.go
├── oa_test.go
├── readme.md
└── step.go

15.1.1 oa_test.go

package _51oa

import (
    "fmt"
    "testing"
)

/*
=== RUN   TestOAStep
流程开始
[hrStep] 开始审批
开始处理
[tlStep] 开始审批
开始处理
[bossStep] 开始审批
开始处理
流程结束
--- PASS: TestOAStep (0.00s)
PASS
*/
func TestOAStep(t *testing.T) {
    o := NewOA()
    fmt.Println("流程开始")
    o.approval()
    fmt.Println("流程结束")
}

15.1.2 oa.go

package _51oa

type oa struct {
    firstStep iStep
}

func NewOA() *oa {
    hr := &hrStep{}
    tl := &tlStep{}
    boss := &bossStep{}

    hr.setNext(tl)
    tl.setNext(boss)
    boss.setNext(nil)

    return &oa{
        firstStep: hr,
    }
}

// 审批
func (o *oa) approval() {
    o.firstStep.handle()
}

15.1.3 step.go

package _51oa

import (
    "fmt"
)

// iStep 是步骤
type iStep interface {
    // 处理
    handle()
    // 设置下一步骤
    setNext(iStep)
}

// 基本步骤, 是基类, 实现通用方法
type baseStep struct {
    // 链上的下一节点
    next iStep
}

// 各具体实现, 可复用的方法
func (s *baseStep) handle() {
    fmt.Println("开始处理")
    if s.next != nil { // 本步骤执行结束后, 传递给下一步骤继续处理
        s.next.handle()
    }
}

// 各具体实现, 可复用的方法
func (s *baseStep) setNext(nxt iStep) {
    s.next = nxt
}

// hr步骤
type hrStep struct {
    baseStep
}

// 重写方法
func (s *hrStep) handle() {
    fmt.Println("[hrStep] 开始审批")
    s.baseStep.handle()
}

// tl步骤
type tlStep struct {
    baseStep
}

func (s *tlStep) handle() {
    fmt.Println("[tlStep] 开始审批")
    s.baseStep.handle()
}

// boss步骤
type bossStep struct {
    baseStep
}

func (s *bossStep) handle() {
    fmt.Println("[bossStep] 开始审批")
    s.baseStep.handle()
}

15.2 敏感词过滤器

如果希望做一个论坛, 由于法律, 广告等原因, 需要敏感词过滤, 如果含敏感词, 则帖子会被封禁

其实各 filter 构成了职责链

├── filter.go
├── filter_test.go
└── readme.md

15.2.1 filter_test.go

package _52sensitive_word_filter

import (
	"github.com/stretchr/testify/require"
	"testing"
)

/*
=== RUN   TestFilter
执行广告过滤
执行广告过滤
执行法律过滤
--- PASS: TestFilter (0.00s)
PASS
*/
func TestFilter(t *testing.T) {
	chain := SensitiveWordFilterChain{}
	chain.AddFilter(&AdFilter{})
	ret := chain.Filter("abc")
	require.True(t, ret)

	chain.AddFilter(&PoliceFilter{})
	ret = chain.Filter("abc")
	require.True(t, ret)
}

15.2.2 filter.go

package _52sensitive_word_filter

import "fmt"

// SensitiveWordFilter 过滤器接口
type SensitiveWordFilter interface {
	Filter(content string) bool
}

// SensitiveWordFilterChain 职责链
type SensitiveWordFilterChain struct {
	filters []SensitiveWordFilter
}

func (c *SensitiveWordFilterChain) AddFilter(f SensitiveWordFilter) {
	c.filters = append(c.filters, f)
}

func (c *SensitiveWordFilterChain) Filter(content string) bool {
	for _, f := range c.filters {
		f.Filter(content)
	}
	return true
}

// AdFilter 广告过滤器
type AdFilter struct{}

func (f *AdFilter) Filter(content string) bool {
	fmt.Println("执行广告过滤")
	return true
}

// PoliceFilter 法律过滤器
type PoliceFilter struct{}

func (f *PoliceFilter) Filter(content string) bool {
	fmt.Println("执行法律过滤")
	return true
}