Skip to content

Instantly share code, notes, and snippets.

@juev
Created March 20, 2025 09:30
Show Gist options
  • Save juev/ae128ac9b4dd95bd7023fe4bc9b8360f to your computer and use it in GitHub Desktop.
Save juev/ae128ac9b4dd95bd7023fe4bc9b8360f to your computer and use it in GitHub Desktop.

Decorator Pattern

Go program pattern 05 : Decorations

What is the Decorator Pattern?

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.

An HTTP-related Example

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().

Pipeline of Multiple Decorators

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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment