Go 中的装饰器函数

Decorator functions in Go

装饰器模式(函数)有many benefits:

It is very useful when a method has many orthogonal concerns... I.e., None of these concerns are related, other than that we wanna do all (or some) of them whenever we call our method. This is where the decorator pattern really helps.

By implementing the decorator pattern we subscribe to the open-closed principal. Our method is open to future extension but closed to future modification. There's a lot of groovy benefits to obeying the open-closed principle.

然而,我找到的所有示例都非常复杂(例如,使用许多中间件编写 HTTP 服务器)。这使我很难在其他地方应用该原则。我需要一些我可以轻松试穿的东西,以便全神贯注。

谁能给我一个最能说明如何在 Go 中使用装饰器模式(函数)的更简单的示例?

This example by Alex Alehano,过于简单,无法投入实际使用。我需要一些可以说明这一点的东西:

func Decorate(c Decorated, ds ...Decorator) Decorated {
    decorated := c
    for _, decorate := range ds {
        decorated = decorate(decorated)
    }
    return decorated
}

根据不同 option/instruction 的字符串操作,例如向上、向下、base64 等,将是 IMO 的最佳示例,并且也添加 prefix/suffix,如“This technique proves especially valuable if the decorators themselves are parameterized".

我知道 decorators/middlewares 的一个很好的例子,它使用了与您展示的相同的技术。虽然这不是特定于字符串操作,但我发现它的制作非常精美,在这里(扫描代码):

https://github.com/basvanbeek/pubsub/blob/master/kafka/publisher.go

看看type PublisherOption func(*publisherConfig) error

您会注意到一些函数 returns PublisherOption 类型。这些是 decorators/middleware.

操作发生在 NewKafkaPublisher 函数上:

func NewKafkaPublisher(
  broker, topic string,
  options ...PublisherOption,
  ) (pubsub.Publisher, error) {
  if len(strings.Trim(broker, " \t")) == 0 {
      return nil, ErrNoBrokers
  }
  brokerHosts := strings.Split(broker, ",")

  if len(topic) == 0 {
      return nil, ErrNoTopic
  }
  // set sensible defaults
  pc := &publisherConfig{
    syncPublisher: defaultSyncPublisher,
    ackMode:       defaultRequiredAcks,
    successes:     nil,
    logger:        log.NewNopLogger(),
    topic:         topic,
    partitioner:   sarama.NewManualPartitioner(topic),
    partition:     0,
  }

 // parse optional parameters
 for _, option := range options {
     if err := option(pc); err != nil {
        return nil, err
     }
 }

请注意,它循环遍历 option 并调用每个 decorator/middleware。

补充示例在这里:https://gist.github.com/steven-ferrer/e4c1eb973a930c2205039448cda6d3d9

可运行代码:https://play.golang.org/p/ZXixnyTHXH

希望有所帮助:)

A string manipulation according to different option/instruction, e.g., to upper, to lower, adding prefix/suffix, to base64, etc, would be the best example IMO.

这是你问的例子:

package main

import (
    "fmt"
    "strings"
)

const (
    prefix = "PREFIX"
    suffix = "SUFFIX"
)

type Decorated=string

func addConstPrefix(s string) string {
    return prefix + s
}

func addConstSuffix(s string) string {
    return s + suffix
}

type Decorator=func(string) string

func Decorate(c Decorated, ds ...Decorator) Decorated {
    decorated := c
    for _, decorator := range ds {
        decorated = decorator(decorated)
    }
    return decorated
}

func main() {

    var toLower Decorator = strings.ToLower
    var toUpper Decorator = strings.ToUpper
    var dec3 Decorator = addConstPrefix
    var dec4 Decorator = addConstSuffix

    input := "Let's decorate me!"
    inputUppercase := Decorate(input, []Decorator{toUpper}...)
    fmt.Println(inputUppercase)

    allDecorators := []Decorator{toUpper, toLower, dec3, dec4}
    output := Decorate(input, allDecorators...)
    fmt.Println(output)
}

注意,为了简单起见,它使用了 Go 1.9 中引入的 Golang 的 类型别名 。这是我们使用的两个别名:

type Decorated=string
type Decorator=func(string)string

以便稍后可以应用您的 Decorate() 函数。然后我们简单地创建一些与我们定义的类型签名相匹配的装饰器(函数):

var toLower Decorator = strings.ToLower
var toUpper Decorator = strings.ToUpper
var dec3 Decorator = addConstPrefix
var dec4 Decorator = addConstSuffix

并应用它们:

allDecorators := []Decorator{toUpper, toLower, dec3, dec4}
output := Decorate(input, allDecorators...)

编辑:

一个参数化装饰器只是一个函数returns另一个函数,例如:

func addPrefix(prefix string) func(string) string {
    return func(s string) string {
        return prefix + s
    }
}

然后可以通过以下方式应用它:

input2 := "Let's decorate me!"
prefixed := Decorate(input2, []Decorator{addPrefix("Well, ")}...)

首先,装饰器基本上是一个函数,它将另一个特定类型的函数作为其参数,returns 一个相同类型的函数。这实质上允许您创建一个函数链。所以在 Go 中它看起来像这样:

// this is the type of functions you want to decorate
type StringManipulator func(string) string

// this is your decorator.
func ToLower(m StringManipulator) StringManipulator {
    return func(s string) string {
        lower := strings.ToLower(s)
        return m(lower)
    }
}

here's a more complete example