Functional Options

这也是一种设计模式,简单的例子就是你对一个接口的设计在第一次真的能一应俱全吗?后期不会多参数等的调整吗?

 

错误的例子,下面的例子每次加参数都十分痛苦。

// 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

posted @ 2020-04-28 16:48  zhangyu63  阅读(216)  评论(0编辑  收藏  举报