golang里面, 经常要使用到 option的配置, 有时候配置项太多, 以至于不能在函数参数列表中进行解决, 如果处理到一个函数列表参数很长的函数, 估计得抽风了.

常见的解决方案:

  1. Config对象, 将参数放到 Config对象中, 但是这样会很臃肿, 尤其是 是否设置和0值的区分度 会变得很模糊. 如果使用指针避免了 0值的问题,那么, 指针的对象 一般意味着 修改的传递性, 那么, 使用指针也会变的疑惑. 实例化传递的指针 在使用过程中变化了, 会产生什么影响? 调用者会很惶恐, 充满着不确定性.

参考的文章1、2中指出了使用Option 的方式进行简化, 通过变长参数的方式 提升了 可配置性、可维护性.

在grpc-go的实现中, option使用了新的方式, 提供了Option对象的配置形式. 比如 DialOptions 这里列出grpc serverOptions的使用方式:

type serverOptions struct {
    creds                 credentials.TransportCredentials
    codec                 baseCodec
    cp                    Compressor
    dc                    Decompressor
    unaryInt              UnaryServerInterceptor
    streamInt             StreamServerInterceptor
    inTapHandle           tap.ServerInHandle
    statsHandler          stats.Handler
    maxConcurrentStreams  uint32
    maxReceiveMessageSize int
    maxSendMessageSize    int
    unknownStreamDesc     *StreamDesc
    keepaliveParams       keepalive.ServerParameters
    keepalivePolicy       keepalive.EnforcementPolicy
    initialWindowSize     int32
    initialConnWindowSize int32
    writeBufferSize       int
    readBufferSize        int
    connectionTimeout     time.Duration
    maxHeaderListSize     *uint32
}

var defaultServerOptions = serverOptions{
    maxReceiveMessageSize: defaultServerMaxReceiveMessageSize,
    maxSendMessageSize:    defaultServerMaxSendMessageSize,
    connectionTimeout:     120 * time.Second,
    writeBufferSize:       defaultWriteBufSize,
    readBufferSize:        defaultReadBufSize,
}

type ServerOption interface {
    apply(*serverOptions)
}

func NewServer(opt ...ServerOption) *Server {
    opts := defaultServerOptions
    for _, o := range opt {
        o.apply(&opts)
    }
    s := &Server{
        lis:    make(map[net.Listener]bool),
        opts:   opts,
        conns:  make(map[io.Closer]bool),
        m:      make(map[string]*service),
        quit:   make(chan struct{}),
        done:   make(chan struct{}),
        czData: new(channelzData),
    }
    s.cv = sync.NewCond(&s.mu)
    if EnableTracing {
        _, file, line, _ := runtime.Caller(1)
        s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
    }

    if channelz.IsOn() {
        s.channelzID = channelz.RegisterServer(&channelzServer{s}, "")
    }
    return s
}

参考

  1. Self-referential functions and the design of options, commandcenter
  2. Functional options for friendly API,, Dave Cheney
  3. [](https://halls-of-valhalla.org/beta/articles/functional-options-pattern-in-go,54/)