问题描述
我有以下界面:
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,您可以实际使用它们。它们将成为您想要做的方式。