首页 > 其他分享 >Golang - Option模式(2)(函数选项模式)

Golang - Option模式(2)(函数选项模式)

时间:2023-04-16 17:12:03浏览次数:52  
标签:选项 return Option int 模式 Server Golang func

函数式选项模式(Functional Options Pattern)

函数式选项模式是一种在 Go 中构造结构体的模式,它通过设计一组非常有表现力和灵活的 API 来帮助配置和初始化结构体。

优缺点

选项模式有很多优点,例如:支持传递多个参数并且在参数发生变化时保持兼容性;支持任意顺序传递参数;支持默认值;方便扩展;通过 WithXXX 的函数命名,可以使参数意义更加明确,等等。 不过,为了实现选项模式,我们增加了很多代码。

适用场景

  • 结构体参数很多,创建结构体时,我们期望创建一个携带默认值的结构体变量,并选择性修改其中一些参数的值。
  • 结构体参数经常变动,变动时我们又不想修改创建实例的函数。例如:结构体新增一个 retry 参数,但是又不想在 NewConnect 入参列表中添加 retry int 这样的参数声明。 函数式选项模式一般应用在那些配置较多,且有可选参数的情况,如果结构体参数比较少,可以慎重考虑要不要采用选项模式。

举例

type Server struct{
  host string
  port int
  timeout time.Duration
  maxConn int
}

func New(host string, port int) *Server {
  return &Server{host, port}
}

func (s *Server) Start() error {
}

如果要扩展 Server 的配置选项,如何做?通常有三种做法:

  • 为每个不同的配置选项声明一个新的构造函数
  • 使用专门的配置结构体来保存配置信息
  • 使用 Functional Option Pattern

方式一:新的构造函数

这种方式配置较少且不太会变化的情况。

func NewWithTimeout(host string, port int, timeout time.Duration) *Server {
  return &Server{host, port, timeout}
}

func NewWithTimeoutAndMaxConn(host string, port int, timeout time.Duration, maxConn int) *Server {
  return &Server{host, port, timeout, maxConn}
}

方式二:使用专门的配置结构体来保存配置信息

这种方式也是很常见的,特别是当配置选项很多时。即使将来增加更多配置选项,也可以轻松的完成扩展,不会破坏 Server 的 API,但当增加或删除选项,需要对 Config 有较大的修改。

type Server struct{
  cfg Config
}

// 通常可以创建一个 Config 结构体,其中包含 Server 的所有配置选项。
type Config struct {
  Host string
  Port int
  Timeout time.Duration
  MaxConn int
}

func New(cfg Config) *Server {
  return &Server{cfg}
}

方式三:使用 Functional Option Pattern

使用这种方式将来增加选项,只需要增加对应的 WithXXX 函数即可。 这种模式,在第三方库中使用挺多,比如 github.com/gocolly/colly

声明

// Option 类型是一个函数类型,它接收一个参数:*Server
type Option func(*Server)

// 定义一系列相关返回 Option 的函数:
func WithHost(host string) Option {
  return func(s *Server) {
    s.host = host
  }
}

func WithPort(port int) Option {
  return func(s *Server) {
    s.port = port
  }
}

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

func WithMaxConn(maxConn int) Option {
  return func(s *Server) {
    s.maxConn = maxConn
  }
}

// Server 的构造函数接收一个 Option 类型的不定参数
func New(options ...Option) *Server {
  svr := &Server{}
  for _, option := range options {
    option(svr)
  }
  return svr
}

使用

package main

import (
  "log"
  "server"
)

func main() {
  svr := New(
    WithHost("localhost"),
    WithPort(8080),
    WithTimeout(time.Minute),
    WithMaxConn(120),
  )
  if err := svr.Start(); err != nil {
    log.Fatal(err)
  }
}

Uber 的 Go 语言编程规范中提到该模式时,建议定义一个 Option 接口,而不是 Option 函数类型。

type options struct {
  cache  bool
  logger *zap.Logger
}

type Option interface {
  apply(*options)
}

type cacheOption bool

func (c cacheOption) apply(opts *options) {
  opts.cache = bool(c)
}

func WithCache(c bool) Option {
  return cacheOption(c)
}

type loggerOption struct {
  Log *zap.Logger
}

func (l loggerOption) apply(opts *options) {
  opts.logger = l.Log
}

func WithLogger(log *zap.Logger) Option {
  return loggerOption{Log: log}
}

// Open creates a connection.
func Open(
  addr string,
  opts ...Option,
) (*Connection, error) {
  options := options{
    cache:  defaultCache,
    logger: zap.NewNop(),
  }

  for _, o := range opts {
    o.apply(&options)
  }

  // ...
}

已有项目改进

img.png img_1.png img_2.png

参考

标签:选项,return,Option,int,模式,Server,Golang,func
From: https://www.cnblogs.com/beatleC/p/17323600.html

相关文章

  • 状态模式
    概述《设计模式》一书中对于“状态模式”的描述如下:允许一个对象在其内部状态发生改变时改变它的行为,使得这个对象看起来像修改了它的类状态模式的UML图如下所示:一般在以下几种情况下使用状态模式:一个对象的行为取决于它的状态一个操作中含有庞大的多分支条件语句,且......
  • 《3D编程模式》写书-第3次记录
    大家好,这段时间我完成了“积木模式”、“管道模式”、“多线程模式”、“依赖隔离模式”的初稿目前我已经完成了所有模式(7个模式)的初稿,下面会给出它们的使用场景,如果您正好能用到,请联系我提前阅读模式的初稿,欢迎大家给出反馈建议,您的大名有机会出现在《3D编程模式》的书中,感谢!我......
  • [Mellanox] 安装MFT并配置网卡为以太网模式
    [Mellanox]安装MFT并配置网卡为以太网模式当我们想要配置Mellanox网卡时,如果出现了下面情况:$sudomststartmst:commandnotfound这说明我们的系统中缺少MFT。MFT是一套管理Mellanox网卡的工具,也是MLEX_OFED中的一部分。如果你不小心搞丢了MFT,可以用下面的方法安装:去......
  • 项目中的观察者模式
    背景目前开发的项目中,出现过许多次观察者模式,且其和普通的观察者模式有些许不同,为了理解而进行整理。项目中的观察者模式类图:流程:脚本订阅消息队列消息,创建Subject,传送解析后的消息给所有观察者,观察者根据消息类型判断是不是自己关心的消息,是的话就把自己注册到Subject中;Subj......
  • FTP数据连接既可能是客户端发起的,也可能是服务器端发起的 主动传输模式和被动传输模式
    主动传输模式和被动传输模式在FTP协议中,控制连接使用周知端口21。相反,数据传输连接的目的端口通常实现无法知道。FTP协议使用一个标准的端口21作为ftp-data端口,但是这个端口只用于连接的源地址是服务器端的情况,在这个端口上根本就没有监听进程。FTP的数据连接和控制连接的方向一般......
  • Spring03_代理模式
    一、静态代理(一)代理模式概述​ 在不改变原始类(或叫被代理类)的情况下,通过引入代理类来给原始类附加功能。一般情况下,我们让代理类和原始类实现同样的接口。但是,如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的。在这种情况下,我们可以通过让代理类继承原始类的方法......
  • 03装饰者模式
    例子星巴兹是以扩张速度最快而闻名的咖啡连锁店。因为扩张速度实在太快,他们着急更新订单系统,来匹配他们的饮料供应要求。实现1---继承购买咖啡时,也可以要求其中加入各种调料,例如:蒸奶,豆浆很明显,星巴兹为自己制造了一个维护噩梦,如果牛奶的价钱上扬,怎么办?新增一种焦糖调料风味......
  • Go For Web:Golang http 包详解(源码剖析)
    前言:本文作为解决如何通过Golang来编写Web应用这个问题的前瞻,对Golang中的Web基础部分进行一个简单的介绍。目前Go拥有成熟的Http处理包,所以我们去编写一个做任何事情的动态Web程序应该是很轻松的,接下来我们就去学习了解一些关于Web的相关基础,了解一些概念,以及......
  • PyQt5 软件在 macOS HiDPI 模式下出现字体模糊的问题
    ​ Retina屏幕是苹果公司在2010年在 WWDC上发布的一种高密度像素的屏幕。HiDPI是一种渲染技术,它可以让Retina屏幕上的图像更加清晰。HiDPI技术会将图像渲染成两倍于原始分辨率的大小,然后再将其缩小到原始分辨率的大小,这样就可以让图像更加清晰。PyQt5编写的软件在Wi......
  • day8 golang-chan-协程-定时器-锁-等待组
    packagemainimport( "fmt" "math/rand" "sync" "sync/atomic" "time")funcexample1(){ //不要这样写,阻塞就死无法解除,零值nil varc1chanint fmt.Printf("%d,%d,%v",len(c1),cap(c1),c1) //c1<-1......