Go program pattern 05 : Decorations
The Decorator pattern is a design pattern that allows us to dynamically add behavior to an object at runtime without altering its implementation. This is achieved by creating a wrapper object or decorator that contains the original object and provides an enhanced interface to add new behavior.
In Go, we can use functions as decorators because Go supports higher-order functions, which means functions can be passed as parameters and returned as values.
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
func WithServerHeader(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Println("--->WithServerHeader()")
w.Header().Set("Server", "HelloServer v0.0.1")
h(w, r)
}
}
func hello(w http.ResponseWriter, r *http.Request) {
log.Printf("Received Request %s from %s\n", r.URL.Path, r.RemoteAddr)
fmt.Fprintf(w, "Hello, World! "+r.URL.Path)
}
func main() {
http.HandleFunc("/v1/hello", WithServerHeader(hello))
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
In this code, we use the Decorator pattern. The WithServerHeader()
function acts as a decorator that takes an http.HandlerFunc and returns a modified version. This example is relatively simple, as we only add a response header using WithServerHeader().
When using multiple decorators, the code can become less visually appealing as we need to nest functions layer by layer. However, we can refactor the code to make it cleaner. To do this, we first write a utility function that iterates through and calls each decorator:
type HttpHandlerDecorator func(http.HandlerFunc) http.HandlerFunc
func Handler(h http.HandlerFunc, decors ...HttpHandlerDecorator) http.HandlerFunc {
for i := range decors {
d := decors[len(decors)-1-i] // iterate in reverse
h = d(h)
}
return h
}
Then, we can use it like this:
http.HandleFunc("/v4/hello", Handler(hello,
WithServerHeader, WithBasicAuth, WithDebugLog))