这也是一种设计模式,简单的例子就是你对一个接口的设计在第一次真的能一应俱全吗?后期不会多参数等的调整吗?
错误的例子,下面的例子每次加参数都十分痛苦。
// package db func Connect( addr string, timeout time.Duration, caching bool, ) (*Connection, error) { // ... } // Timeout and caching must always be provided, // even if the user wants to use the default. db.Connect(addr, db.DefaultTimeout, db.DefaultCaching) db.Connect(addr, newTimeout, db.DefaultCaching) db.Connect(addr, db.DefaultTimeout, false /* caching */) db.Connect(addr, newTimeout, false /* caching */)
那么总不能每次都改各种调用等吧,这完全没有工程性和可维护性。所以一些高手就想出了fucntional options。
type options struct { timeout time.Duration caching bool } // Option overrides behavior of Connect. type Option interface { apply(*options) } type optionFunc func(*options) func (f optionFunc) apply(o *options) { // 这里就是执行withTimeout这边返回的OptionFunc的逻辑了===>也就是调用o.timeout = t f(o) } func WithTimeout(t time.Duration) Option { // 这里类似int(12),只是为了返回一个optionFunc return optionFunc(func(o *options) { o.timeout = t }) } func WithCaching(cache bool) Option { return optionFunc(func(o *options) { o.caching = cache }) } // Connect creates a connection. func Connect( addr string, opts ...Option, ) (*Connection, error) { options := options{ // 理论上我们需要加上默认的参数,比如一个server timeout: defaultTimeout, caching: defaultCaching, } for _, o := range opts { o.apply(&options) } // ... } // Options must be provided only if needed. db.Connect(addr) db.Connect(addr, db.WithTimeout(newTimeout)) db.Connect(addr, db.WithCaching(false)) db.Connect( addr, db.WithCaching(false), db.WithTimeout(newTimeout), )
例子二
http://legendtkl.com/2016/11/05/code-scalability/
func NewServer(addr string, options ...func(*Server)) (*Server, error) { srv := &Server{ Addr: addr, } for _, option := range options { option(srv) } return srv } func timeout(d time.Duration) func(*Server) { return func(srv *Server) { srv.timeout = d } } func tls(c *config) func(*Server) { return func(srv *Server) { Tls := loadConfig(c) srv.tls = Tls } } //使用 src, err = NewServer("localhost:8080", timeout(1), tls(path/to/cert))
原博还展示了一种似乎更便捷的维护handler方式,这样就可以避免我们每一次添加新的处理分散处理。
这样我们只需先写一个hanler逻辑,再register,再调用mux相关使用即可。
type Option struct { Key string } var mux map[string]func(option *Option) error func register(key string, f func(option *Option) error) error { if mux == nil { mux = make(map[string]func(option *Option) error) } if _, exist := mux[key]; exist { return errors.New("handler exist") } mux[key] = f return nil } func factory(option *Option) error { return mux[option.Key](option) }
end