Golang中责任链模式
链式模式是许多设计模式之一,用于编写更好、更健壮的代码。
这种模式就像一个生产链,其中链条的每个环节都负责一个具体的任务。当链条开始时,首先执行其任务,然后,在没有错误的情况下,传递给下一个环节,直到最后一个负责人。在那个时候,链条结束。
此外,负责人可以接收输入并返回输出,这个输出将会是下一个负责人的输入。或者(在某些场景中更好),输入被修改为在所有负责人之间共享的通用DTO。
创建基本责任
对于这种情况,最好使用抽象类,但golang不是一种OOP语言,所以为了模拟这个,必须使用接口和基础结构体。
package chain
type Chain[T any] interface {
Perform(T) error
}
type Responsible[T any] interface {
Next(T) error
Apply(T) error
}
type BaseResponsible[T any] struct {
next Responsible[T]
}
func (r *BaseResponsible[T]) Next(i T) error {
if r.next == nil {
return nil
}
if err := r.next.Apply(i); err != nil {
return err
}
return r.next.Next(i)
}
func (r *BaseResponsible[T]) SetNext(n Responsible[T]) {
r.next = n
}
上述代码展示了两个重要的接口:Chain和Responsible。
Chain为执行责任链提供了一个容器。所以,它的职责是触发第一个负责人。
Responsible为链的责任环节提供了“执行和下一个”。apply方法将执行负责人的任务。next方法将调用(传递)到下一个负责人。
BaseResponsible结构体,结合Responsible接口,提供了一个抽象类。其中next方法实现使apply方法成为抽象方法(待实现)。
实现责任链
package responsibles
type Triangle struct {
A int
B int
C float64
}
type basePythagoreanTheorem struct {
chain.BaseResponsible[*Triangle]
}
type ASideResponsible struct {
basePythagoreanTheorem
}
func (r *ASideResponsible) Apply(t *Triangle) error {
t.A = rand.Intn(100)
return nil
}
type BSideResponsible struct {
basePythagoreanTheorem
}
func (r *BSideResponsible) Apply(t *Triangle) error {
t.B = rand.Intn(100)
return nil
}
type CSideResponsible struct {
basePythagoreanTheorem
}
func (r *CSideResponsible) Apply(t *Triangle) error {
t.C = math.Sqrt(float64(t.A*t.A + t.B*t.B))
return nil
}
type PythagoreanTheoremChain struct {
start Responsible[*Triangle]
}
func (p PythagoreanTheoremChain) Perform(t *Triangle) error {
if err := p.start.Apply(t); err != nil {
return err
}
return p.start.Next(t)
}
func NewPythagoreanTheoremChain() chain.Chain[*Triangle] {
c := &CSideResponsible{}
b := &BSideResponsible{}
b.SetNext(c)
a := &ASideResponsible{}
a.SetNext(b)
return &PythagoreanTheoremChain{start: a}
}
上述实现将根据毕达哥拉斯定理创建一个三角形。随机选择A和B边,然后通过定理计算C边(斜边)。
其他方法
当然,可以采取一些其他方法,如转移链(前一个的输出是下一个的输入)或完全独立的责任链。
此外,在前面的例子中,这种模式的使用非常简单,但它可以用于更多、更大的事情。例如,数据收集。
数据收集可能会很混乱,需要从许多外部服务(REST, DB, SOAP等)请求数据,并执行一些内部/外部变化的任务,并不容易实现。
在这些场景中,这种模式非常有用。这是因为它允许将步骤分解成完全隔离的责任(它们可以有自己的日志记录器、客户端、配置等),并且链的处理是顺序的,所以链可以根据“在执行这个任务之前必须有什么数据”来设计。

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接
本文链接:http://choupangxia.com/2024/01/02/golang-chained-invocations/