装饰模式(函数)有many benefits:
当方法有许多正交问题时,这是非常有用的...即,这些问题都不相关,除了我们想要在我们调用方法时做所有(或一些)它们。这是装饰模式真正有用的地方。
通过实现装饰器模式,我们订阅了open-closed主体。我们的方法对未来的扩展开放,但对未来的修改是封闭的。遵守开放封闭原则有很多令人遗憾的好处。
但是,我发现的所有示例都非常复杂(例如,编写带有许多中间件的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
}
根据不同选项/指令的字符串操作,例如,对于upper,to lower,to base64等,将是最好的示例IMO,并且还添加前缀/后缀,作为“This technique proves especially valuable if the decorators themselves are parameterized”。
答案 0 :(得分:8)
首先,装饰器基本上是一个函数,它将特定类型的另一个函数作为其参数,并返回相同类型的函数。这基本上允许您创建一系列功能。所以在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)
}
}
答案 1 :(得分:4)
根据不同的选项/指令进行字符串操作,例如,向上,降低,添加前缀/后缀,到base64等,将是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)
}
请注意,为简单起见,它使用Golang的类型别名,这是Go 1.9中引入的。这些是我们使用的两个别名:
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...)
修改强>
参数化装饰器只是一个返回另一个函数的函数,例如:
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, ")}...)
答案 2 :(得分:3)
我知道一个很好的装饰器/中间件示例,它使用了您演示的相同技术。这不是特定于字符串操作,但我发现它非常精美,这里是(扫描代码):
https://github.com/basvanbeek/pubsub/blob/master/kafka/publisher.go
查看type PublisherOption func(*publisherConfig) error
您会注意到某些函数返回PublisherOption
类型。这些是装饰者/中间件。
行动发生在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
并调用每个装饰器/中间件。
补充示例如下:https://gist.github.com/steven-ferrer/e4c1eb973a930c2205039448cda6d3d9
可运行代码:https://play.golang.org/p/ZXixnyTHXH
希望有所帮助:)