前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go编程模式 - 5.函数式选项

Go编程模式 - 5.函数式选项

作者头像
junedayday
发布2021-08-05 13:06:35
2130
发布2021-08-05 13:06:35
举报
文章被收录于专栏:Go编程点滴

注:本文的灵感来源于GOPHER 2020年大会陈皓的分享,原PPT的链接可能并不方便获取,所以我下载了一份PDF到git仓,方便大家阅读。我将结合自己的实际项目经历,与大家一起细品这份文档。

目录

  • 一个常见的HTTP服务器
  • 拆分可选配置
  • 函数式选项
  • 更进一步

ServerConfig

我们先来看看一个常见的HTTP服务器的配置,它区分了2个必填参数与4个非必填参数

代码语言:javascript
复制
type ServerCfg struct {
 Addr     string        // 必填
 Port     int           // 必填
 Protocol string        // 非必填
 Timeout  time.Duration // 非必填
 MaxConns int           // 非必填
 TLS      *tls.Config   // 非必填
}

// 我们要实现非常多种方法,来支持各种非必填的情况,示例如下
func NewServer(addr string, port int) (*Server, error)                                   {}
func NewTLSServer(addr string, port int, tls *tls.Config) (*Server, error)               {}
func NewServerWithTimeout(addr string, port int, timeout time.Duration) (*Server, error) {}
func NewTLSServerWithMaxConnAndTimeout(addr string, port int, maxconns int, timeout time.Duration, tls *tls.Config) (*Server, error) {}

SplitConfig

编程的一大重点,就是要 分离变化点和不变点。这里,我们可以将必填项认为是不变点,而非必填则是变化点。

我们将非必填的选项拆分出来。

代码语言:javascript
复制
type Config struct {
 Protocol string
 Timeout  time.Duration
 MaxConns int
 TLS      *tls.Config
}

type Server struct {
 Addr string
 Port int
 Conf *Config
}

func NewServer(addr string, port int, conf *Config) (*Server, error) {
 return &Server{
  Addr: addr,
  Port: port,
  Conf: conf,
 }, nil
}

func main() {
 srv1, _ := NewServer("localhost", 9000, nil)

 conf := Config{Protocol: "tcp", Timeout: 60 * time.Second}
 srv2, _ := NewServer("localhost", 9000, &conf)

 fmt.Println(srv1, srv2)
}

到这里,其实已经满足大部分的开发需求了。那么,我们将进入今天的重点。

Functional Option

代码语言:javascript
复制
type Server struct {
 Addr     string
 Port     int
 Protocol string
 Timeout  time.Duration
 MaxConns int
 TLS      *tls.Config
}

// 定义一个Option类型的函数,它操作了Server这个对象
type Option func(*Server)

// 下面是对四个可选参数的配置函数
func Protocol(p string) Option {
 return func(s *Server) {
  s.Protocol = p
 }
}

func Timeout(timeout time.Duration) Option {
 return func(s *Server) {
  s.Timeout = timeout
 }
}

func MaxConns(maxconns int) Option {
 return func(s *Server) {
  s.MaxConns = maxconns
 }
}

func TLS(tls *tls.Config) Option {
 return func(s *Server) {
  s.TLS = tls
 }
}

// 用到了不定参数的特性,将任意个option应用到Server上
func NewServer(addr string, port int, options ...Option) (*Server, error) {
 // 先填写默认值
 srv := Server{
  Addr:     addr,
  Port:     port,
  Protocol: "tcp",
  Timeout:  30 * time.Second,
  MaxConns: 1000,
  TLS:      nil,
 }
 // 应用任意个option
 for _, option := range options {
  option(&srv)
 }
 return &srv, nil
}

func main() {
 s1, _ := NewServer("localhost", 1024)
 s2, _ := NewServer("localhost", 2048, Protocol("udp"))
 s3, _ := NewServer("0.0.0.0", 8080, Timeout(300*time.Second), MaxConns(1000))

 fmt.Println(s1, s2, s3)
}

耗子哥给出了6个点,但我感受最深的是以下两点:

  1. 可读性强,将配置都转化成了对应的函数项option
  2. 扩展性好,新增参数只需要增加一个对应的方法

那么对应的代价呢?就是需要编写多个Option函数,代码量会有所增加。

如果大家对这个感兴趣,可以去看一下Rob Pike的这篇blog 。

Further

顺着耗子叔的例子,我们再思考一下,如果配置的过程中有参数限制,那么我们该怎么办呢?

首先,我们改造一下函数Option

代码语言:javascript
复制
// 返回错误
type OptionWithError func(*Server) error

然后,我们改造一下其中两个函数作为示例

代码语言:javascript
复制
func Protocol(p string) OptionWithError {
 return func(s *Server) error {
  if p == "" {
   return errors.New("empty protocol")
  }
  s.Protocol = p
  return nil
 }
}

func Timeout(timeout time.Duration) Option {
 return func(s *Server) error {
  if timeout.Seconds() < 1 {
   return errors.New("time out should not less than 1s")
  }
  s.Timeout = timeout
  return nil
 }
}

我们再做一次改造

代码语言:javascript
复制
func NewServer(addr string, port int, options ...OptionWithError) (*Server, error) {
 srv := Server{
  Addr:     addr,
  Port:     port,
  Protocol: "tcp",
  Timeout:  30 * time.Second,
  MaxConns: 1000,
  TLS:      nil,
 }
 // 增加了一个参数验证的步骤
 for _, option := range options {
  if err := option(&srv); err != nil {
   return nil, err
  }
 }
 return &srv, nil
}

改造基本到此完成,希望能给大家带来一定的帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Go编程点滴 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • ServerConfig
  • SplitConfig
  • Functional Option
  • Further
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档