golang里的options模式最早是由Dave Cheney在这里提出的。options模式提供了一种在创建object时候传入可选参数的方法。

这里总结一下原版的options模式,和我在工作里学到的一个trick,是一个改良版的options模式。

这里用传入一个logger object的场景做为例子。

原版

定义Option object:

type Option func(s *server)

func WithLogger(l *zap.Logger) Option {
	return func(s *server) {
		s.logger = l
	}
}

定义server:

type server struct {
	logger *zap.Logger
	// other fields
}

func newServer(opts ...Option) *server {
	// By default use the dev logger, can be orverrided by the options.
	l, err := zap.NewDevelopment()
	if err != nil {
		panic(err)
	}
	s := &server {
		logger: l,
		// ...
	}
	for _, opt := range opts {
		opt(s)
	}
	return s
}

使用option pattern构建server并传入一个custom logger object:

s := newServer(WithLogger(prodLogger))

改良版

改进版本就是把default value挪到option object文件里,使得原本的constructor更加干净。

具体做法如下:

type Option func(opts *options)

type options struct {
	logger *zap.Logger
}

type newOptions(opts ...Option) *options {
	l, err := zap.NewDevelopment()
	if err != nil {
		panic(err)
	}
	o := &options{
		logger: o,
	}
	for _, opt := range opts {
		opt(o)
	}
	return o
}

这样construct的定义变得更加简洁:

func newServer(opts ...Option) *server {
	options := newOptions(opts...)
	return &server{ logger: options.logger }
}