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

问题描述 投票:1回答:1

在下面的简化代码示例中,我们使用供应商的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中重复很多通用代码(预检查,错误处理,重试循环等等)。由于我们要处理30多个资源,并且每个资源都有自己的创建,读取,更新,销毁API调用函数,因此我们将相同的代码复制了100多次。

我想将所有通用的东西移到某个genericApiCaller(),并将调用主体作为一个参数和名称,或者将指向API调用函数的指针作为第二个参数。

问题1:callCreateAcallCreateB需要不同类型的“主体”。如果它们使用interface {}会更容易,但是这些功能来自供应商SDK,并且修改它们并不明智(更新原因等)。如何动态地将genericApiCaller()的“ body interface {}”声明为“ body requestBodyA”或“ body requestBodyB”?在Go中无法进行运行时断言,但是我没有找到任何好的解决方案。

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

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

go reflection wrapper assertion
1个回答
0
投票

您可以尝试扩展供应商类型以使用诸如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)
}

但是,我认为第二个不太优雅。

© www.soinside.com 2019 - 2024. All rights reserved.