Pattern to invoke multiple methods with varying arguments via reflection

Background
I have a package that has a bunch of function calls to get/set data via API calls. I’m looking at implementing a middleware that will pick up jobs from a queue where each job will provide the name of the method and the respective method arguments for the package. The middleware is then responsible for calling the corresponding functions from the package.

The method is a JSON string and the arguments is a JSON array of strings.

Here are some example function signatures for the app package:

type App struct {}
...

func(a *App) GetUser(username string){}

func(a *App) CreateGroup(groupname string){}

func(a *App) RemoveUserFromGroup(username, groupname string) (err){}

The middleware begins by getting messages from the queue and spins up a goroutine to process the message via Process() function as shown below:

func (m *Middleware) Process(a *App, method string, args []interface{}) {

  switch method {

    case "GetUser", "CreateGroup":

    	m.Call(method, arg[0])

    case "RemoveUserFromGroup":
       username := arg[0]
       groupname := arg[0]
       m.Call(method, arg[0], arg[1])

    // More case statements
    ...

The Call() function uses the reflect package invoke the methods with supplied arguments at run time and does the needful.

Question

As you may have noticed the the Process() function will have many case statements to satisfy:

  1. The right number of arguments are passed
  2. Given the arguments from the queue is a slice, there is a need to construct objects/types e.g structs as arguments for some methods.

A further constraint I have is I can’t change the app package functions. e.g: argument types etc.

Although a switch/case statement works, they seem like an inelegant way to deal with the requirement. I was interested to know if there is a better pattern available to handle these sort of cases?

1 Like

You could try a

map[string]func(...interface{})

to map methods to functions and a second

map[string]int

to map methods to the number of arguments to take.

Example:

package main

import (
	"fmt"
)

func foo(args ...interface{}) {
	fmt.Printf("in foo: %s\n", args)
}

func bar(args ...interface{}) {
	fmt.Printf("in bar: %s\n", args)
}

var m = map[string]func(...interface{}){"foo": foo, "bar": bar}

var c = map[string]int{"foo": 1, "bar": 2}

func call(name string, args ...interface{}) {
	m[name](args[0:c[name]]...)
}

func main() {
	args := []interface{}{"hi", "ho"}

	call("foo", args...)
	call("bar", args...)
}

Output:

in foo: [hi]
in bar: [hi ho]

https://play.golang.org/p/Q09deR6U2vV

Thanks. I forgot to mention that there is constraint on changing the app package function signatures so I can’t make the type an interface.

It’s an empty interface. There shouldn’t be any constraints since it takes in any value.

My comment was regarding changing the function signatures:

A further constraint I have is I can’t change the app package functions. e.g: argument types etc.

If I understood the sample code correctly, it requires taking arguments of arbitrary length and making it of type interface.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.