如何避免包装函数中的动态断言



在下面的简化代码示例中,我们使用供应商的Go库执行数十个不同的API调用。

供应商库为我们提供了API调用体的定义:

// body of api call A
type requestBodyA struct {
aa string
bb int32
}
// body of api call B
type requestBodyB struct {
id string
}

以及为我们执行调用的功能:

// fn performs api call A
func callCreateA(body requestBodyA) {
fmt.Println("Value of body is: ", body)
// makes api call using body requestBodyA
}
// fn performs api call B
func callCreateB(body requestBodyB) {
fmt.Println("Value of body is: ", body)
// makes another api call using body requestBodyB
}

我们使用这些来创建我们自己的CRUD函数,在那里我们读取配置文件并执行创建/读取/更新/删除调用:

func createA() {
bodyA := requestBodyA{
aa: "asdasfsd",
bb: 15,
}
// api retry loop
for i := 1; i <= 3; i++ {
// do some pre-checks (generic)
callCreateA(bodyA)
// check for errors (generic)
// check if error is recoverable (generic)
// if not return error (generic)
// if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
}
}
func createB() {
bodyB := requestBodyB{
id: "someUniqueId",
}
for i := 1; i <= 3; i++ {
// do some pre-checks (generic)
callCreateB(bodyB)
// check for errors (generic)
// check if error is recoverable (generic)
// if not return error (generic)
// if yes use the for loop and try the call again in 1,2,3,5,8,13, .. seconds (generic)
}
}

正如您在函数createAcreateB中所看到的,我们在创建的每个CRUD fn中都重复了许多通用代码(预检查、错误处理、重试循环等等(。当我们处理30多个资源,每个资源都有自己的创建、读取、更新、销毁API调用函数时,我们将相同的代码复制100多次。

我想将所有的泛型移到一些genericApiCaller((中,并将调用主体作为一个参数,将名称或指向API调用函数的指针作为第二个参数。

问题1:callCreateAcallCreateB需要不同类型的"主体"。如果他们使用接口{}会容易得多,但这些函数来自供应商SDK,修改它们是不明智的(更新原因等(。如何将genericApiCaller((的"body接口{}"动态断言为"body requestBodyA"或"body requestBodyB"?运行时断言在Go中是不可能的,但我没有找到任何好的解决方案

问题2:如何将具有不同类型参数的函数作为参数传递给genericApiCaller((?

https://play.golang.org/p/incf-NEaSJ3

您可以尝试扩展供应商类型以使用类似CallCreate接口的东西:

type myBodyA requestBodyA
func (b myBodyA) CallCreate() {
callCreateA((requestBodyA)(b))
}
type myBodyB requestBodyB
func (b myBodyB) CallCreate() {
callCreateB((requestBodyB)(b))
}
type CallCreate interface {
CallCreate()
}
func main() {
var ifs CallCreate
ifs = myBodyA{
aa: "aa",
bb: 15,
}
for i := 1; i <= 3; i++ {
ifs.CallCreate()
}
ifs = myBodyB{
id: "someUniqueId",
}
for i := 1; i <= 3; i++ {
ifs.CallCreate()
}

你也可以使用类似的东西

func genericApiCall(api,body interface{}) {
av := reflect.ValueOf(api)
av.Call([]reflect.Value{reflect.ValueOf(body)})
}
func main() {
bodyA := requestBodyA{
aa: "asdasfsd",
bb: 15,
}
genericApiCall(callCreateA,bodyA)
bodyB := requestBodyB{
id: "someUniqueId",
}
genericApiCall(callCreateB,bodyB)
}

然而,在我看来,第二个并不那么优雅。

最新更新