2

I am getting one concern on multi layer if-else-if condition so I want to make short by using a map.

Please see below code in if-else-if which I want to replace with a map.

function, args := APIstub.GetFunctionAndParameters()

if function == "queryProduce" {
    return s.queryProduce(APIstub, args)
} else if function == "initLedger" {
    return s.initLedger(APIstub)
} else if function == "createProduce" {
    return s.createProduce(APIstub, args)
} else if function == "queryAllProduces" {
    return s.queryAllProduces(APIstub)
} else if function == "changeProduceStatus" {
    return s.changeProduceStatus(APIstub, args)
}

return shim.Error("Invalid Smart Contract function name.")
}
icza
  • 389,944
  • 63
  • 907
  • 827
Abhirock
  • 425
  • 6
  • 17

2 Answers2

6

For what you have a switch would be nice:

switch function {
case "queryProduce":
    return s.queryProduce(APIstub, args)
case "initLedger":
    return s.initLedger(APIstub)
case "createProduce":
    return s.createProduce(APIstub, args)
case "queryAllProduces":
    return s.queryAllProduces(APIstub)
case "changeProduceStatus":
    return s.changeProduceStatus(APIstub, args)
}

Using a map would be inconvenient because not all your methods have the same signature, but you could use multiple maps.

Yet another solution could be to use reflection to call the methods, but again, handling the different arguments would be inconvenient. Reflection is also slower, not to mention you'd have to take care of not to allow calling methods not intended to be exposed. For an example, see Call functions with special prefix/suffix.

icza
  • 389,944
  • 63
  • 907
  • 827
1

It is possible to express what you have as a map. The basic setup here is, no matter which path you go down, you get some function that you can call with no parameters, and it always returns the same type (error). I might explicitly pass the args in.

The high-level structure of this is to have a map of function names to functions, then call the selected function.

funcMap := map[string]func([]string) error{...}
funcName, args := APIstub.GetFunctionAndParameters()
f := funcMap[funcName]
if f == nil {
    f = func(_ []string) error {
        return shim.Error("Invalid Smart Contract function name.")
    }
}
return f(args)

The map syntax gets kind of verbose

funcMap := map[string]func([]string) error{
    "queryProduce": func(args []string) error {
        return s.queryProduce(APIstub, args)
    },
    "initLedger": func(_ []string) error {
        return s.initLedger(APIstub)
    },
}

The map approach is better if you're ever going to call this in multiple places, or you want a separate validation step that some name would be defined if used, or if the actual list of functions is dynamic (you can add or remove things from the map at runtime). The inconsistent method signatures do introduce a complication and making everything consistent would help here (make functions like initLedger take an argument list even if it's unused).

In ordinary code I'd expect the switch form from @icza's answer to be more idiomatic.

David Maze
  • 130,717
  • 29
  • 175
  • 215