链式模式是许多设计模式之一,用于编写更好、更健壮的代码。

这种模式就像一个生产链,其中链条的每个环节都负责一个具体的任务。当链条开始时,首先执行其任务,然后,在没有错误的情况下,传递给下一个环节,直到最后一个负责人。在那个时候,链条结束。

此外,负责人可以接收输入并返回输出,这个输出将会是下一个负责人的输入。或者(在某些场景中更好),输入被修改为在所有负责人之间共享的通用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等)请求数据,并执行一些内部/外部变化的任务,并不容易实现。

在这些场景中,这种模式非常有用。这是因为它允许将步骤分解成完全隔离的责任(它们可以有自己的日志记录器、客户端、配置等),并且链的处理是顺序的,所以链可以根据“在执行这个任务之前必须有什么数据”来设计。



Golang中责任链模式插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2024/01/02/golang-chained-invocations/