Golang装饰器函数的参数有哪些?

huangapple go评论77阅读模式
英文:

Which arguments for Golang decorator functions

问题

我想在AdminAPI的几个方法(如Update)上使用一个setter。为此,我创建了一个方法类型,可以与其他方法匹配。

我应该使用接口而不是func type吗?

type AdminAPI struct {
}

type ToAdminCtx func(ctx context.Context, req interface{}) (interface{}, error)

func (a AdminAPI) AdminM2MSetter(s ToAdminCtx) ToAdminCtx {
	return func(ctx context.Context, arg interface{}) (interface{}, error) {
		m2mPrincipal, _ := a.GetM2MPrincipal(ctx)
		ctxM2M := extlib.SetPrincipal(ctx, m2mPrincipal)
		return s(ctxM2M, arg)
	}
}

func (a AdminAPI) Update(ctx context.Context, req *ReqType) (RespType, error) {}
updateWithAdminCtx := a.adminAPI.AdminM2MSetter(s.adminAPI.Update)
// ERROR => cannot use s.adminAPI.Update (value of type func(ctx 
// context.Context, req *ReqType) (RespType, error)) as grpcAdmin.ToGetAdminCtx value in 
// argument to s.adminAPI.AdminM2MSetter

_, err := updateWithAdminCtx(ctx context.Context, req *ReqType)
英文:

I would like to use a setter on several methods of AdminAPI such as Update. To do so I created a method type that could match with other methods.

Should I use interface instead of func type?

type AdminAPI struct {
}

type ToAdminCtx func(ctx context.Context, req interface{}) (interface{}, error)

func (a AdminAPI) AdminM2MSetter(s ToAdminCtx) ToAdminCtx {
	return func(ctx context.Context, arg interface{}) (interface{}, error) {
		m2mPrincipal, _ := a.GetM2MPrincipal(ctx)
		ctxM2M := extlib.SetPrincipal(ctx, m2mPrincipal)
		return s(ctxM2M, arg)
	}
}

func (a AdminAPI) Update(ctx context.Context, req *ReqType) (RespType, error) {}
updateWithAdminCtx := a.adminAPI.AdminM2MSetter(s.adminAPI.Update)
// ERROR => cannot use s.adminAPI.Update (value of type func(ctx 
// context.Context, req *ReqType) (RespType, error)) as grpcAdmin.ToGetAdminCtx value in 
// argument to s.adminAPI.AdminM2MSetter

_, err := updateWithAdminCtx(ctx context.Context, req *ReqType)

答案1

得分: 1

你得到的错误信息已经很明显了:

a.adminAPI.AdminM2MSetter(s.adminAPI.Update)

这里调用了

func (a AdminAPI) AdminM2MSetter(s ToAdminCtx) ToAdminCtx {

s.adminAPI.Update作为参数传入,而它期望的类型是ToAdminCtx。而你定义的ToAdminCtx类型是这样的:

type ToAdminCtx func(ctx context.Context, req interface{}) (interface{}, error)

然而你的Update函数的第二个参数是*ReqType类型,第一个返回值是RespType类型,因此Update不是一个ToAdminCtx类型。ToAdminCtx函数类型是一个可以接受上下文和任意类型的函数。你的Update函数不能保证在所有情况下都能正常工作。

你想要的是一种方法来“包装”任何函数,在ctx参数上进行一些操作(可能是设置某个值),然后继续调用。在Go 1.19之前,我们可以通过添加某种包装类型来实现这一点,例如:

type Wrapper struct {
    UpdateReqType *ReqType
    AnotherType *ReqType2 // 用于包装其他要包装的调用
}

然后修改所有相关的函数,比如你的Update函数,使其接受包装类型的参数:

func (a AdminAPI) Update(ctx context.Context, req Wrapper) (Resp, error) {
    realReq := req.UpdateReqType // 获取实际使用的请求
}

响应类型可能也需要类似地进行包装和/或组合。

现在,Go支持泛型,这是一个可以非常有用的情况,让我们将你的AdminM2MSetter函数改为这样:

func AdminM2MSetter[T any, R any](s func(context.Context, T) (R, error)) func(context.Context, T) (R, error) {
    return func (ctx context.Context, arg T) (R, error) {
        m2mPrincipal, _ := a.GetM2MPrincipal(ctx)
        ctxM2M := extlib.SetPrincipal(ctx, m2mPrincipal)
        return s(ctxM2M, arg)
    }
}

这样,我们只需要定义这个函数一次,然后依靠编译器为我们生成适合所有需要的类型的定制函数。对于你的Update函数,我们可以这样做:

a.adminAPI.AdminM2MSetter[*ReqType, RespType](s.adminAPI.Update)

将泛型的TR类型替换为Update函数使用的具体类型。因为我不太清楚你想要以这种方式包装哪些函数,我使用了T any, R any,但是根据我看到的情况,你似乎想要包装某种类型的请求处理程序,你可以创建自己的约束:

type Requests interface {
    *ReqType1 | *ReqType2 | *ReqType3 // 以此类推
}
type Responses interface {
    Resp1 | Resp2 | Resp3
}

然后将[T any, R any]替换为[T Requests, R Responses]

英文:

The error you're getting is pretty self-explanatory I think:

a.adminAPI.AdminM2MSetter(s.adminAPI.Update)

This is calling

func (a AdminAPI) AdminM2MSetter(s ToAdminCtx) ToAdminCtx {

Passing in s.adminAPI.Update as the argument, which is expected to be of the type ToAdminCtx. That type you is defined as:

type ToAdminCtx func(ctx context.Context, req interface{}) (interface{}, error)

Yet your Update function's second argument is a *ReqType, and its first return value is a RespType value, and therefore Update is not a ToAdminCtx. The ToAdminCtx function type is a function that can be called with a context and literally ANY type. Your Update function cannot be guaranteed to work in all cases the ToAdminCtx function can.

What you're looking for is a way to "wrap" any function, add do some work on the ctx argument (probably setting some value), and then pass on the call. Before go 1.19, we did this by adding some kind of wrapper types like this:

type Wrapper struct {
    UpdateReqType *ReqType
    AnotherType *ReqType2 // for some other call you want to wrap
}

The change all the relevant functions, like your Update function to take the wrapper argument type:

func (a AdminAPI) Update(ctx context.Context, req Wrapper) (Resp, error) {
    realReq := req.UpdateReqType // get the actual request used here
}

The response types would either be similarly wrapped and/or composed.

Now, though, go supports generics, and this is a situation where they can be quite useful, let's change your AdminM2MSetter function to something like this:

func AdminM2MSetter[T any, R any](s func(context.Context, T) (R, error)) func(context.Context, T) (R, error) {
    return func (ctx context.Context, arg T) (R, error) {
        m2mPrincipal, _ := a.GetM2MPrincipal(ctx)
        ctxM2M := extlib.SetPrincipal(ctx, m2mPrincipal)
        return s(ctxM2M, arg)
    }
}

This way, we only have to define this function once, but rely on the compiler to generate a tailor-made function for all the types we need. In case of your Update function, we'd do something like this:

a.adminAPI.AdminM2MSetter[*ReqType, RespType](s.adminAPI.Update)

Essentially replacing the generic T and R types with the specific types used by your Update function. Because I don't really know what functions you want to wrap in this way, I used T any, R any, but because it looks to me like you're trying to wrap request handlers of some sort, you could create your own constraints:

type Requests interface {
    *ReqType1 | *ReqType2 | *ReqType3 // and so on
}
type Responses interface {
    Resp1 | Resp2 | Resp3
}

And just replace [T any, R any] with [T Requests, R Responses]

huangapple
  • 本文由 发表于 2023年3月28日 17:05:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75864283.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定