Go语言精进之路-白明 | 变长参数函数的妙用
All

Go语言精进之路-白明 | 变长参数函数的妙用

MrSnake
2022-03-14 / 6 评论 / 388 阅读 / 正在检测是否收录...

实现功能选项模式

在日常Go编程中,我们经常会实现一些带有设置选项的创建型函数。对于一些复杂的Go包中的创建型函数,它要提供的可设置选项有时多达数十种,甚至后续还会增加。那么在选项增长的同时,依旧保持对外接口的稳定性,就成了关键。
例:实现一个NewFinishedHouse函数,其中选项有:

  • 装修风格
  • 是否是中央空调
  • 地面材料
  • 墙面材料
    还有可能会增加的选项。

版本1实现:使用结构体封装配置选项

type FinishedHouse struct {
    style     string
    isCentral bool
    floor     string
    wall    string
}
type Option struct {
    Style     string
    IsCentral bool
    Floor     string
    Wall    string
}

func NewFinishedHouse(option *Option) *FinishedHouse {
    var (
        style = "fashion"
        isCentral = true
        floor = "normal"
        wall = "standard"
    )
    if option != nil {
        style = option.Style
        isCentral = option.IsCentral
        floor = option.Floor
        wall = option.Wall
    }
    return &FinishedHouse{
        style: style,
        isCentral: isCentral,
        floor: floor,
        wall: wall,
    }
}

采用这种方式的好处是,当选项增加时,只需增加结构体option的字段,原本的函数签名是不变的。

版本2实现:使用功能选项模式

Go语言之父Rob Pike早在2014年就在其博文“自引用函数与选项设计”[2]中论述了一种被后人称为“功能选项”(functional option)的模式,这种模式应该是目前进行功能选项设计的最佳实践。

type FinishedHouse struct {
    style     string
    isCentral bool
    floor     string
    wall    string
}

type Option func(*FinishedHouse)

func NewFinishedHouse(options ...Option) *FinishedHouse {
    h := &FinishedHouse{
        style: "fashion",
        isCentral: true,
        floor: "normal",
        wall: "standard",
    }
    for _, option := range options {
        option(h)
    }
    return h
}

func WithStyle(style string) Option {
    return func(f *FinishedHouse) {
        f.style = style
    }
}

func WithIsCentral(isCentral bool) Option {
    return func(f *FinishedHouse) {
        f.isCentral = isCentral
    }
}

func WithFloor(floor string) Option {
    return func(f *FinishedHouse) {
        f.floor = floor
    }
}

func WithWall(wall string) Option {
    return func(f *FinishedHouse) {
        f.wall = wall
    }
}

func main() {
   fmt.Printf("%+v", NewFinishedHouse()) // 默认选项
   fmt.Printf("%+v", NewFinishedHouse(
       WithStyle("classic"),
       WithIsCentral(false),
   ))
}

我们看到在该方案中,FinishedHouse的配置选项不是通过存储在结构体中的配置参数传入的,而是通过对FinishedHouse值本身进行操作的函数调用(利用函数的“一等公民”特质)实现的,并且通过使用变长参数函数,我们可以随意扩展传入的配置选项的个数。

0

评论

博主关闭了所有页面的评论