Skip to content

Instantly share code, notes, and snippets.

@slok
Created June 12, 2016 10:09
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save slok/d60a6bde03e69d6af97b25277a08c051 to your computer and use it in GitHub Desktop.
Save slok/d60a6bde03e69d6af97b25277a08c051 to your computer and use it in GitHub Desktop.
handler & handlerfunc golang pattern example
// Package main is an example of how handler pattern works in golang.
//
// At first you will need some sort of start point. To do this we create
// ExampleHandler interface, this interface has a trigger method that will
// execute the chain, in his case is RunExample, it accepts a writer, and a
// custom input object, as you see there is an out and an in parameter.
//
// We could work like this you can create multiple ExampleHandlers in a helper
// function and call them on in another. But this is not very handy and it smells
//
// Here comes the handlers pattern, this pattern is used in a very important
// piece of the core lib of Go, the HttpHandler and HttpHandlerFunc. The
// explanation is simple.
//
// as we have said, we have an interface with a triggering method. So we are
// going to create a new type called ExamplHandlerFunc that is of type function
// this type function is the type of the triggering funcion of ExampleHandler,
// in this case RunExample(*ExampleWriter, *ExampleInpu).
// doing this we can implement the interface of ExampleHandler calling itself.
// This is the RunExample implementation of ExampleHandlerFunc will call itself
//
// Now we can create ExampleHandlerFunctions with functions that have logic very
// easily, so this means that we can create easily funcionts that call other
// functions, in other words, a chain.
//
// To do this we can create custom functiosn that create ExampleHandlerFuncs with
// logic and context, like we did on historyIDExample. It a accepts an ID and a
// handler as paramter, tis second param is importanto so it will allow us to
// create a chain. It creates a ExampleHandlerFunc object using this context
// uses the ID for whatever and calls the handler of the argument. Doing this
// repeteadly will create a chain of handlerfuncs calling handlerfuncs.
// At some point you will need to stop the chain, this is easy, you only need
// to don't call the received ExampleHandlerFunc parameter.
package main
import "fmt"
//ExampleWriter is the object that will be the result of the chain, implements writer
type ExampleWriter struct {
History []string
}
func (e *ExampleWriter) Write(data []byte) (int, error) {
e.History = append(e.History, string(data))
return len(data), nil
}
// ExampleInput helper object for the input data in handlerfuncs, could be a
// simple string or `type ExampleInput string`
type ExampleInput struct {
Data string
}
// ExampleHandler is the interface that will be implemeted by the handlerFunc type
type ExampleHandler interface {
RunExample(*ExampleWriter, *ExampleInput)
}
// ExampleHandlerFunc type will be a function compatible with RunExample so
// it can execute itself, in other words, it implements ExampleHandler interface
// by executing itself
type ExampleHandlerFunc func(*ExampleWriter, *ExampleInput)
// RunExample will execute itself (remember this object is a function)
func (e ExampleHandlerFunc) RunExample(ew *ExampleWriter, in *ExampleInput) {
e(ew, in)
}
// historyExample will add history to our handlers
func historyIDExample(id int, eh ExampleHandler) ExampleHandler {
return ExampleHandlerFunc(func(ew *ExampleWriter, in *ExampleInput) {
fmt.Fprintf(ew, "Start example %d: %s", id, in.Data)
defer fmt.Fprintf(ew, "Finish example %d: %s", id, in.Data) // We could use defer!
eh.RunExample(ew, in)
})
}
func strExample(start, end string, eh ExampleHandler) ExampleHandler {
return ExampleHandlerFunc(func(ew *ExampleWriter, in *ExampleInput) {
if start != "" {
fmt.Fprint(ew, start)
}
if end != "" {
defer fmt.Fprint(ew, end)
}
eh.RunExample(ew, in)
})
}
func main() {
end := ExampleHandlerFunc(func(*ExampleWriter, *ExampleInput) {})
he := strExample("Starting...", "End!",
historyIDExample(1,
historyIDExample(2,
historyIDExample(3,
historyIDExample(4,
historyIDExample(5,
historyIDExample(6, end)))))))
w := ExampleWriter{History: []string{}}
he.RunExample(&w, &ExampleInput{Data: "This is an example"})
for _, h := range w.History {
fmt.Println(h)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment