13

I know there are some questions and posts/articles regarding this question, but from my newbie view, not exactly. The thing is, I've got a main program listening to a port and redirecting the calls to a specific handler. The typical structure:

func main() {
    http.HandleFunc("/something", specificHandler)
    http.ListenAndServe(":8080", nil)
}

With the handler being something like:

func specificHandler(w http.ResponseWriter, r *http.Request) {
    somepackage.foo()
}

Then somepackage, which contains the function foo, has some global variables, basically because they're needed for functions to share (e.g., when using a priority queue implemented with a container/heap, which will get the priorities in the Swap function from a global matrix of distances which is of course changable). And many other examples. In summary, global variables...

The problem is, as you might see, that those variables are shared among all the calls to the handler. And that's bad.

How can I actually solve this? There must be an easy to way to do it that I haven't got to yet, because it looks like something so usual...

Thanks in advance.


EDIT

To make it clearer. For example, in my A* package, I've got the following global variables:

var openVerticesAS PriorityQueueAStar

// which vertices are closed
var closedVertices map[int]bool

// which vertices are currently open
var openVertices map[int]bool

// cost from start to node
var gScore map[int]float64

// cost from start to end, passing by node i (g+h)
var fScore map[int]float64

Then, PriorityQueueAStar is implemented as follows:

type PriorityQueueAStar []int // rel id

func (pq PriorityQueueAStar) Len() int { return len(pq) }

func (pq PriorityQueueAStar) Empty() bool { return len(pq) == 0 }

func (pq PriorityQueueAStar) Less(i, j int) bool {
    return fScore[pq[i]] < fScore[pq[j]]
}

func (pq PriorityQueueAStar) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
}

func (pq *PriorityQueueAStar) Push(x interface{}) {
    *pq = append(*pq, x.(int))
}

func (pq *PriorityQueueAStar) Pop() interface{} {
    old := *pq
    n := len(old)
    rel := old[n-1]
    *pq = old[0 : n-1]
    return rel
}

func (pq PriorityQueueAStar) Top() interface{} {
    return pq[0]
}

The question then, is, how do I keep doing this without having all those maps as global variables? If they are part of the struct, how do I access the struct from the priority queue functions?

jcasado94
  • 185
  • 1
  • 1
  • 7
  • The fact that you recognize you have variables being modified between different calls indicates it's the design. Sounds like you want some sort of container. You'll want to design a struct to fit your needs, and pass that struct in to calls to your functions in somepackage. – william.taylor.09 Apr 20 '16 at 20:42
  • Yes, that sounds good, or creating a struct for the whole package and let it contain the variables. But what about functions like Less(i, j int) from the heap interface? Those will need to access the struct and I don't see a way to pass it to them... – jcasado94 Apr 20 '16 at 20:55
  • Look into channels [chan](https://golang.org/ref/spec#Channel_types) they solve a *lot* of problems associated with sharing memory. – miltonb Apr 20 '16 at 20:55
  • You wouldn't need to pass the functions. You would define a struct type that implements the sort functions. Then you can do foo.Sort() wherever you need to. @jcasado94 if you could provide the type of data you're using, that might help us come up with a good solution. Check out http://stackoverflow.com/a/28999886/4746361 to get started. – william.taylor.09 Apr 20 '16 at 20:56
  • The package basically runs some path finding algorithms and has to answer with the paths. Therefore the variables are mainly priority queues implementing the container/heap interface and maps of open/closed vertices and so on, and of course a Graph struct... – jcasado94 Apr 20 '16 at 21:11

2 Answers2

21

When your handler needs a variable, usually that means you should implement the Handler interface instead of providing a HandlerFunc function.

Here is a BAD example (using global variables):

var globalThing string

func specificHandler(w http.ResponseWriter, r *http.Request) {
    w.Write(globalConfigThing)
}

func main() {
    globalThing = "Hello world!"
    http.HandleFunc("/something", specificHandler)
    http.ListenAndServe(":8080", nil)
}

Here is a BETTER example (not using global variables):

type specificHandler struct {
    Thing string
}

func (h *specificHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write(h.Thing)
}

func main() {
    http.Handle("/something", &specificHandler{Thing: "Hello world!"})
    http.ListenAndServe(":8080", nil)
}

As you can see, a Handler can encapsulate variables.


For completeness, the other approach is to use a function closure. This works well for once-off handlers but is not re-usable and is harder to write unit tests for.

func main() {
    scopedThing := "Hello world!"
    http.HandleFunc("/something", func (w http.ResponseWriter, r *http.Request) {
        w.Write(scopedThing)
    })
    http.ListenAndServe(":8080", nil)
}

Done correctly, you can now avoid global variables in your package somepackage by passing them as arguments, etc.

EDIT: For example, you can define your handler struct with several PriorityQueueAStar fields from the somepackage package:

type specificHandler struct {
    QueueA somepackage.PriorityQueueAStar
    QueueB somepackage.PriorityQueueAStar
    QueueC somepackage.PriorityQueueAStar
}

func (h *specificHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.QueueA.Push(h.QueueB.Pop)
    h.QueueB.Push(h.QueueC.Pop)
    w.Write([]byte("Queues pushed and popped"))
}
Black Frog
  • 11,595
  • 1
  • 35
  • 66
chowey
  • 9,138
  • 6
  • 54
  • 84
  • 1
    That's exactly what I was missing. Handlers as structs encapsulating variables. Thanks, gotta work on it now! ;) – jcasado94 Apr 20 '16 at 21:30
  • Sorry, quick question. I've managed to add to the struct all the functions to implement a container/heap priority queue inside it (Len, Push, Pop,...), and it works well for one. But what if I need to implement two or more container/heap objects inside the same struct? How can I define those functions for all of them? Thanks. – jcasado94 Apr 20 '16 at 22:22
  • Embed a struct inside another struct. – elithrar Apr 21 '16 at 04:28
  • Very good answer and examples! However, in my go1.12.5 it gives the error _"cannot use &specificHandler literal (type *specificHandler) as type func(http.ResponseWriter, *http.Request) in argument to http.HandleFunc"_. How to make it working again? – xpt Jun 04 '19 at 01:58
  • 1
    Use `http.Handle` instead of `http.HandleFunc` if you are providing a struct that implements `http.Handler`. – chowey Jun 04 '19 at 02:21
1

The closure method was mentioned by chowey, but had a caveat that it isn't testable or reusable. It is actually testable and reusable if you have a function to return the closure. For some types of data this may make for a cleaner abstraction and implementation:

func handleThing(thing string) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Write(thing)
    }
}

func main() {
    http.HandleFunc("/something", handleThing("Hello world!"))
    http.ListenAndServe(":8080", nil)
}
Winder
  • 1,984
  • 5
  • 23
  • 33