Created
June 12, 2016 10:09
-
-
Save slok/d60a6bde03e69d6af97b25277a08c051 to your computer and use it in GitHub Desktop.
handler & handlerfunc golang pattern example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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