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等)请求数据,并执行一些内部/外部变化的任务,并不容易实现。
在这些场景中,这种模式非常有用。这是因为它允许将步骤分解成完全隔离的责任(它们可以有自己的日志记录器、客户端、配置等),并且链的处理是顺序的,所以链可以根据“在执行这个任务之前必须有什么数据”来设计。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接
本文链接:https://choupangxia.com/2024/01/02/golang-chained-invocations/