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

How to avoid dynamic assertion in wrapper functions?

在下面的简化代码示例中,我们使用供应商提供的 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 函数,我们在其中读取配置文件并执行 create/read/update/delete 调用:

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 调用函数的指针作为第二个参数。

问题一:callCreateAcallCreateB需要不同类型的"body"。如果它们使用 interface{} 会更容易,但这些函数来自供应商 SDK,修改它们并不明智(更新原因等)。如何动态地将 genericApiCaller() 的 "body interface{}" 断言为 "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)
}

不过,在我看来,第二种就没那么优雅了。