Go使用选项模式的优点是什么?

作者站长头像
站长
· 阅读数 4

之前在写一个限流器的时候,习惯性的用了选项模式来配置

type LeakyLimiter struct {
    lock     sync.Mutex
    last     time.Time
    sleep    time.Duration
    perReq   time.Duration
    maxSlack time.Duration
}

type leakyOptions struct {
    maxSlack time.Duration
}

type LeakyOption interface {
    apply(*leakyOptions)
}

type leakyOptionFunc func(options *leakyOptions)

func (l leakyOptionFunc) apply(o *leakyOptions) {
    l(o)
}

func WithMaxSlack(maxSlack time.Duration) LeakyOption {
    return leakyOptionFunc(func(options *leakyOptions) {
        options.maxSlack = maxSlack
    })
}

func NewLeakyLimiter(rate int, opts ...LeakyOption) *LeakyLimiter {
    options := leakyOptions{
        maxSlack: -10 * time.Second / time.Duration(rate),
    }

    for _, option := range opts {
        option.apply(&options)
    }

    return &LeakyLimiter{
        perReq:   time.Second / time.Duration(rate),
        maxSlack: options.maxSlack,
    }
}

但是突然想到,能不能直接暴露出能够修改可配置项的函数

type LeakyLimiter struct {
    lock     sync.Mutex
    last     time.Time
    sleep    time.Duration
    perReq   time.Duration
    maxSlack time.Duration
}

func (l *LeakyLimiter) WithMaxSlack(maxSlack time.Duration) *LeakyLimiter {
    l.maxSlack = maxSlack
    return l
}

func NewLeakyLimiter(rate int) *LeakyLimiter {
    return &LeakyLimiter{
        perReq:   time.Second / time.Duration(rate),
        maxSlack: -10 * time.Second / time.Duration(rate),
    }
}

相比于这种直接配置的方式,选项模式的优点在哪里呢?感觉扩展性和可读性也没有提升,这种方式也可以链式调用。我个人想的可能是不暴露修改的接口,在调用构造函数以后就不会被修改了安全一点。除了这一个以外还有其他的考量吗?希望听听各位大大的分析

回复
1个回答
avatar
test
2024-06-25

个人经验来参考:

  • 因为 Go 的常驻内存,一种是初始化启动、还一种是按需启动。
  • 常驻内存下,多个依赖的服务,其实希望的是 *LeakyLimiter 是一致的,如果改成对象调整,当 A、B 两个依赖这个服务的指针会因为内部变化而影响另外一个。

关于第二点的补充:比如,c2c1 因为调用的是同一个 l,因为 l.WithCount(3) 导致 l 会互相影响

package main

import "fmt"

func main() {
    l := NewLimiter(5)

    c1 := NewCase(l)

    c2 := NewCase(l.WithCount(3))

    fmt.Println(c1, c2)
}

type Case struct {
    limiter *Limiter
}

func NewCase(limiter *Limiter) *Case {
    return &Case{
        limiter: limiter,
    }
}

func (c *Case) Allow() bool {
    return c.limiter.Allow()
}

type Limiter struct {
    Count int
}

func NewLimiter(count int) *Limiter {
    return &Limiter{
        Count: count,
    }
}

func (l *Limiter) WithCount(c int) *Limiter {
    l.Count = c

    return l
}

func (l *Limiter) Allow() bool {
    return l.Count > 0
}
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容