问题
Decorator pattern (functions) has 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.
However, all the examples that I found are really complicated (e.g., writing HTTP servers with many middlewares). This make it difficult for me to apply the principle elsewhere. I need something that I can easily try on so as to wrap my head around it.
Can someone give me an simpler example that can best illustrate how to do Decorator pattern (functions) in Go please?
This example by Alex Alehano, is too simple to be put into practical use. I need something that can illustrate this:
func Decorate(c Decorated, ds ...Decorator) Decorated {
decorated := c
for _, decorate := range ds {
decorated = decorate(decorated)
}
return decorated
}
A string manipulation according to different option/instruction, e.g., to upper, to lower, to base64, etc, would be the best example IMO, and adding prefix/suffix too, as "This technique proves especially valuable if the decorators themselves are parameterized".
回答1:
First of all, a decorator is basically a function that takes another function of a specific type as its argument and returns a function of the a same type. This essentially allows you to create a chain of functions. So in Go it would look something like this:
// 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
回答2:
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.
This is the example you asked about:
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)
}
Note that for the sake of simplicity, it uses Golang's type aliases, which were introduced in Go 1.9. These are the two aliases we are using:
type Decorated=string
type Decorator=func(string)string
so that your Decorate()
function could later be applied. Then we are simply creating few decorators (functions) which match the type signature we defined:
var toLower Decorator = strings.ToLower
var toUpper Decorator = strings.ToUpper
var dec3 Decorator = addConstPrefix
var dec4 Decorator = addConstSuffix
and apply them:
allDecorators := []Decorator{toUpper, toLower, dec3, dec4}
output := Decorate(input, allDecorators...)
EDIT:
A parametrized decorator would simply be a function which returns another function, for example:
func addPrefix(prefix string) func(string) string {
return func(s string) string {
return prefix + s
}
}
It could then be applied in the following way:
input2 := "Let's decorate me!"
prefixed := Decorate(input2, []Decorator{addPrefix("Well, ")}...)
回答3:
I know a very good example of decorators/middlewares that utilizes the same technique that you demonstrated. This is not specific to string manipulation though, but I find it really beautifully crafted, here it is (scan through the code):
https://github.com/basvanbeek/pubsub/blob/master/kafka/publisher.go
Take a look at the type PublisherOption func(*publisherConfig) error
You will notice that some of the functions returns PublisherOption
type. These are the decorators/middleware.
The action happens on NewKafkaPublisher
function:
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
}
}
Notice that it loops through the option
and calls each decorator/middleware.
Supplemental example is here: https://gist.github.com/steven-ferrer/e4c1eb973a930c2205039448cda6d3d9
Runnable code: https://play.golang.org/p/ZXixnyTHXH
Hope that helped :)
来源:https://stackoverflow.com/questions/45944781/decorator-functions-in-go