Golang链模式

链模式( Chain Pattern)是用于编写更好、更健壮的代码的众多设计模式之一。

这种模式的工作原理类似于链式生产,链中的每个环节都负责一项具体任务。当链启动时,第一个执行其任务,然后,如果没有错误,则传递到下一个,直到最后一个负责人。到那时,链条就结束了。

 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 等)中请求数据,还要执行对内部/外部进行某种更改的任务,这可不是一件容易的事。

在这种情况下,这种模式大放异彩。这是因为它允许将步骤分离成完全隔离的负责步骤(它们之间可以有自己的日志记录器、客户端、配置等)(正如 SOLID 所喜欢的那样),并且链的过程是有顺序的,因此可以根据 "在执行此任务之前必须使用哪些数据 "来设计链。