golang设计模式——职责链模式

职责链模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fokJYhdk-1660574053651)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220815221234932.png)]

职责链将处理模块串联成链,请求沿着链条被处理,提供了很好的扩展性,而且能够去掉if-else。

职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UA5ElcP4-1660574053652)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220815221610616.png)]

分析

职责链模式的定义已经将模式说的比较清楚了,一个请求需要被多个对象进行处理,可以将处理对象连成一条链,挨个处理请求。

连成链的方式比较多样,可以用UML中展示的那样,一个处理对象使用SetSuccessor引用下一个处理对象。也可以使用array或者list存储所有处理对象,使用循环方式遍历。

对于第二种方式,是否感觉有些像观察者两者具体实现、目的都差不多,主要区别在观察者模式中的处理对象功能可能完全不相似,而且观察者模式主要负责将信息传递给处理对象即可。职责链模式的处理对象功能一般相似,另外职责链模式也关注请求是否正确被处理。

另外,定义里说”直到有一个对象处理它“也不太准确,有多少对象可以处理请求看具体需求,极端情况下每一个对象都可以处理请求。

职责链模式的核心在于将处理对象整理成链路。

应用场景

如果请求被多个对象进行处理,就可以用职责链模式。具体业务的像敏感词脱敏,框架中的过滤器、拦截器等。

总体感觉框架中使用的比较多一些,研发人员能够快速扩展出自己的过滤器和拦截器

代码实现

我们仿照Gin,实现Gin的全局中间件功能。

package main

import "fmt"

var status int8 = 0

type HandlerFunc func()

type HandlersChain []HandlerFunc

/**
 * @Description:
 */
type RouterGroup struct {
   Handlers HandlersChain
   index    int8
}

/**
 * @Description: 添加中间件,将其组成链式
 * @receiver group
 * @param middleware
 */
func (group *RouterGroup) Use(middleware ...HandlerFunc) {
   group.Handlers = append(group.Handlers, middleware...)
}

/**
 * @Description: 链顺序执行
 * @receiver group
 */
func (group *RouterGroup) Next() {
   for group.index < int8(len(group.Handlers)) {
      group.Handlers[group.index]()
      group.index++
   }
}

/**
 * @Description: 中间件
 */
func middleware1() {
   fmt.Println("全局中间件1执行完毕")
}

/**
 * @Description: 中间件
 */
func middleware2() {
   fmt.Println("全局中间件2执行失败")
   status = 1
}

func main() {
   r := &RouterGroup{}
   //添加中间件
   r.Use(middleware1, middleware2)
   //运行中间件
   r.Next()
   //状态检查
   if status == 1 {
      fmt.Println("中间件检查失败,请重试")
      return
   }
   //执行后续流程
}

输出:

➜ myproject go run main.go

全局中间件1执行完毕

全局中间件2执行失败

中间件检查失败,请重试

这是一个简版的中间件执行过程,我将Gin中的Context和RouterGroup合并了。虽然比起真正的执行流程缺乏很多内容,但是核心操作是一致的。

实例

首先我们看一下一个简单的实现模板,然后我们再看看实际上我们常用 web 框架 gin 当中是如何处理请求的

代码

// Package chain 职责链模式
// 🌰 假设我们现在有个校园论坛,由于社区规章制度、广告、法律法规的原因需要对用户的发言进行敏感词过滤
//    如果被判定为敏感词,那么这篇帖子将会被封禁
package chain

// SensitiveWordFilter 敏感词过滤器,判定是否是敏感词
type SensitiveWordFilter interface {
	Filter(content string) bool
}

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

// AddFilter 添加一个过滤器
func (c *SensitiveWordFilterChain) AddFilter(filter SensitiveWordFilter) {
	c.filters = append(c.filters, filter)
}

// Filter 执行过滤
func (c *SensitiveWordFilterChain) Filter(content string) bool {
	for _, filter := range c.filters {
		// 如果发现敏感直接返回结果
		if filter.Filter(content) {
			return true
		}
	}
	return false
}

// AdSensitiveWordFilter 广告
type AdSensitiveWordFilter struct{}

// Filter 实现过滤算法
func (f *AdSensitiveWordFilter) Filter(content string) bool {
	// TODO: 实现算法
	return false
}

// PoliticalWordFilter 政治敏感
type PoliticalWordFilter struct{}

// Filter 实现过滤算法
func (f *PoliticalWordFilter) Filter(content string) bool {
	// TODO: 实现算法
	return true
}

单元测试

package chain

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSensitiveWordFilterChain_Filter(t *testing.T) {
	chain := &SensitiveWordFilterChain{}
	chain.AddFilter(&AdSensitiveWordFilter{})
	assert.Equal(t, false, chain.Filter("test"))

	chain.AddFilter(&PoliticalWordFilter{})
	assert.Equal(t, true, chain.Filter("test"))
}

Gin 的中间件实现

我们直接看一下 gin Context 的实现,其中 Next() 方法就是主要的执行方法,这里其实就是我们最上面说到的职责链模式的变体,因为它会在每一个处理函数中进行处理,而不是第一个接收到就停止了

type Context struct {
    // ...

    // handlers 是一个包含执行函数的数组
    // type HandlersChain []HandlerFunc
	handlers HandlersChain
    // index 表示当前执行到哪个位置了
	index    int8

    // ...
}

// Next 会按照顺序将一个个中间件执行完毕
// 并且 Next 也可以在中间件中进行调用,达到请求前以及请求后的处理
// Next should be used only inside middleware.
// It executes the pending handlers in the chain inside the calling handler.
// See example in GitHub.
func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)
		c.index++
	}
}

总结

通过Gin中间件的例子,可以很好证明职责链的扩展性。简单使用Use增加自己创建的中间件,每一个请求都会被新增的中间件所处理。所以开发者可以方便的增加鉴权、限流、脱敏、拦截等操作。这就是所谓的优雅吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是几种常见的Golang设计模式: 1. 工厂模式(Factory Pattern):用于创建对象的模式,通过定义一个创建对象的接口来实现对象的实例化。 ```go type Shape interface { Draw() } type Circle struct{} func (c *Circle) Draw() { fmt.Println("Drawing a circle") } type Rectangle struct{} func (r *Rectangle) Draw() { fmt.Println("Drawing a rectangle") } type ShapeFactory struct{} func (sf *ShapeFactory) GetShape(shapeType string) Shape { if shapeType == "circle" { return &Circle{} } else if shapeType == "rectangle" { return &Rectangle{} } return nil } func main() { factory := &ShapeFactory{} circle := factory.GetShape("circle") circle.Draw() // 输出:Drawing a circle rectangle := factory.GetShape("rectangle") rectangle.Draw() // 输出:Drawing a rectangle } ``` 2. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。 ```go type Singleton struct{} var instance *Singleton func GetInstance() *Singleton { if instance == nil { instance = &Singleton{} } return instance } func main() { singleton1 := GetInstance() singleton2 := GetInstance() fmt.Println(singleton1 == singleton2) // 输出:true } ``` 3. 观察者模式(Observer Pattern):定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。 ```go type Subject struct { observers []Observer } func (s *Subject) Attach(observer Observer) { s.observers = append(s.observers, observer) } func (s *Subject) Notify() { for _, observer := range s.observers { observer.Update() } } type Observer interface { Update() } type ConcreteObserver struct{} func (co *ConcreteObserver) Update() { fmt.Println("Observer is updated") } func main() { subject := &Subject{} observer := &ConcreteObserver{} subject.Attach(observer) subject.Notify() // 输出:Observer is updated } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值