Decorator functions in Go

xpt picture xpt · Aug 29, 2017 · Viewed 11.2k times · Source

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".

Answer

mkopriva picture mkopriva · Aug 29, 2017

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