Did you also see this post?
It is basically the HTTP.Handler example generalized to any functions.
In a nutshell:
Consider you want to decorate this function:
func Do(a, b int) bool {
fmt.Println("Do")
return a == b
}
Do decorate this function, first define a function type that has Do
’s signature.
type DoFunc func(int, int) bool
Now you can write a decorator function…
func decorate(f DoFunc) DoFunc {
return func(a, b int) bool {
fmt.Println("pre processing")
res := f(a, b)
fmt.Println("post processing")
return res
}
}
…and use it in place of Do
:
decoratedDo := decorate(Do)
decoratedDo(4, 7)
(Note that the orignal post seems to have accidentally renamed their decorator function from decoratedLike to likeStats at this point, which makes their example a bit confusing.)
In a similar way, you can also decorate interface functions.
Consider io.Writer
, which is an interface:
// definition from the standard library
type Writer interface {
Write(p []byte) (n int, err error)
}
An interface provides no implementation for its functions. Any type that has a method that implements all functions of an interface is said to satisfy the interface.
Consider, for example, this function that takes an io.Writer
:
func Save(name []byte, w io.Writer) {
n, err := w.Write(name)
// do the error handling
}
*os.Stdout
implements io.Write()
and therefore is an io.Writer
; hence we can pass an *os.Stdout
to Save
:
Save("Gopher", os.Stdout)
You can easily decorate any interface. First, create a struct that contains that interface…
type DecoratedWriter struct {
w io.Writer
}
…and then implement all functions of that interface as wrapper functions:
func (d *DecoratedWriter) Write (p []byte) (int, error) {
fmt.Println("pre processing")
n, err := d.w.Write(p)
fmt.Println("post processing")
return n, err
}
Finally, create a New...
function that takes any type that implements the interface and returns an instance of your struct that wraps this type.
func NewDecoratedWriter(w io.Writer) *DecoratedWriter {
return &DecoratedWriter{w}
}
With this, you now can pass a decorated writer to Save
, in place of the *os.File
used before:
dw := NewDecoratedWriter(os.Stdout)
Save("Gopher", dw)
Runnable code in the playground.