如何在Go中概括函数包装?

问题描述

我有以下界面:

type Selector interface {
    SelectOne(ctx context.Context,one A) (Result,error)
    SelectTwo(ctx context.Context,one A,two B) ([]Result,error)
    SelectThree(ctx context.Context,two B,three C) ([]Result,error)
}

和以下实现:

type Database struct{}
func (d Database) SelectOne(...) (...) {...}
func (d Database) SelectTwo(...) (...) {...}
func (d Database) SelectThree(...) (...) {...}

然后,最重要的是,我想添加一个使用非常漂亮的github.com/hashicorp/golang-lru库的缓存层:

type SelectorCache struct {
    db        Database
    cacheOne *lru.Cache
    cacheTwo *lru.Cache
}

func (c SelectorCache) SelectOne(ctx context.Context,error) {
    cached,ok := c.cacheOne.Get(makeKey(one))
    if ok {
        casted,ok := cached.(Result)
        if ok {
            return casted,nil
        }
    }
    fetched,err := c.db.SelectOne(ctx,one)
    if err != nil {
        return Result{},err
    }
    c.cache.Add(key,fetched)
    return fetched,nil
}

func (c SelectorCache) SelectTwo(ctx context.Context,error) {
    ...
        casted,ok := cached.([]Result)
    ...
    fetched,err := c.db.SelectTwo(ctx,one,two)
    ...
}

func () SelectThree(ctx context.Context,err := c.db.SelectThree(ctx,two,three)
    ...
}

如您所见,每种情况下的缓存层基本相同,唯一的区别在于底层函数。如果是Python,我可以轻松创建将* a,** kw传递给包装函数的包装函数。我该如何重写,以使样板消失?

解决方法

您可以编写一个可变参数函数(请参见Function types),该函数以任意数量的int作为参数(零个或多个),并一次性处理它们。例如

func (d Database) Select(ctx context.Context,numbers ...int)

您可以在numbers的循环中遍历range并执行所需的操作。您的函数调用可以保持与以前相同。

fetched,err := c.db.Select(ctx,one)
fetched,one,two)
fetched,two,three)
,

您在评论中提到参数类型有所不同。

通常,您可以这样做:

  • 在编译时,或
  • 在运行时。

运行时版本更易于编码和使用,并且非常灵活,但是当然会花费一些运行时成本。也许您正在尝试避免这种情况(很好,但是请记住关于在优化之前进行测量的古老说法)。

您在示例中编写的是编译时版本。

我该如何重写,以使样板消失?

对于Go 1,只有一种方法可以做到:编写程序来编写程序。 {这就是go generate的全部内容。也有一个Go blog post

在Go 2中,几乎可以肯定会有generics,您可以实际使用它们。它们将成为您想要做的方式。