func (c *continuousUpdateEipList) Update() (err error) {
for _, fn := NewEipListUpdate().steps {
err := fn()
if err != nil {
common.Log.Warn(...)
return err
}
}
return
}
type Step func() error
type EipListUpdate struct {
err error
steps [3]Step
}
func NewEipListUpdate() *EipListUpdate {
var (
eipList []EipInfo // 被函数引用形成闭包
startTime = time.Now()
)
initData := func() (err error) {
eipList, err = initEipList()
return
}
insertData := func() (err error) {
err = insertEipList(eipList)
return
}
insertAbnormalEvent := func() (err error) {
err = insertEipAbnormalEvent(eipList, startTime)
return
}
return &EipListUpdate{
steps: [3]Step{initData, insertData, insertAbnormalEvent},
}
}
以限流的 interceptor 为例,支持传入自定义的限流器,就需要定义以限流器为参数的高阶函数,返回框架需要的 interceptor,并在 interceptor 函数内使用传入的限流器判断是否需要限流
type Limiter interface {
Limit(key string) bool
}
func UnaryServerInterceptor(limiter Limiter) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if limiter.Limit(info.FullMethod) {
return nil, status.Errorf(codes.ResourceExhausted, "%s is rejected by grpc_ratelimit middleware, please retry later.", info.FullMethod)
}
return handler(ctx, req)
}
}
目前传入的参数是固定的,可以这么来实现。更进一步,如果使用比较复杂,除了当前已经确定的参数,可以预期以后会增加更多的参数。也就要求当前设计的接口需要有很好的扩展性,使用 Functional Options
type Options struct {
byMethod bool
byUser bool
byClientIP bool
}
type Option func(*Options)
// 定义修改配置的一组函数
func ByMethod(m bool) Option {
return func(o *options) {
o.byMethod = m
}
}
func ByUser(u bool) Option {
return func(o *options) {
o.byUser = u
}
}
func ByClientIP(c bool) Option {
return func(o *options) {
o.byClientIP = c
}
}
func UnaryServerInterceptor(limiter Limiter, opts ...Option) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
default := options {
byMethod: true,
byUser: false,
byClientIP: false,
}
for _, opt := range opts {
opt(&default)
}
...
return handler(ctx, req)
}
}
type ListAppsOpts struct {
limit int
offset int
hasDeployed bool
}
ListApps(ListAppOpts{limit: 5, offset: 0)
ListApps(ListAppOpts{limit: 5, offset: 0, hasDepolyed: true})
**陷阱:**hasDeployed 是布尔类型,这意味着如果不为其提供任何值时,程序总是会使用布尔类型的零值(zero value):false
。所以,现在的代码其实根本拿不到 “未按已部署状态过滤” 的结果,hasDeployed
要么为 true
,要么为 false
,不存在其他状态
为解决上面的问题,最直接的做法是引入 pointer type 和普通的值类型不同,Go 的指针类型拥有一个特殊的零值 nil 因此只要把 hasDeployed
从布尔类型 bool 改成指针类型 *bool
type ListAppOpts struct {
limit int
offset int
hasDeployed *bool
}
func ListApps(opts ListAppOpts) []App {
if opts.hasDeployed == nil {
// 默认不过滤的分支
} else {
//按 hasDeployed == true or false 过滤
}
...
return allApps[opts.offset : opts.offset+opts.limit]
}
除了普通传参模式外,Go 语言其实还支持可变数量的参数,使用该特性的函数统称为 “可变参数函数(varadic functions)”。比如 append
、fmt.Println
均属此类
为了实现 “函数式选项” 模式,我们首先修改 ListAppss
函数的签名,使其接收类型为 func(*ListAppsOptions)
的可变数量参数