Go创建对象时,如何优雅的传递初始化参数?这里所说的优雅,指的是:
- 支持传递多个参数
- 参数个数、类型发生变化时,尽量保持接口的兼容性
- 参数支持默认值
- 具体的参数可根据调用方需关心的程度,决定是否提供默认值
Go 并不像c++和python那样,支持函数默认参数。所以使用Go时,我们需要一种方便、通用的手法来完成这件事。
Go的很多开源项目都使用Option模式,但各自的实现可能有些许细微差别。
本文将通过一个渐进式的demo示例来介绍Option模式,以及相关的一些思考。本文将内容切分为 10 个小模块,如果觉得前面的铺垫冗余,想直接看 Option 模式的介绍,可以从小标题七开始阅读。
零
先看demo,一开始我们的代码是这样的:
type Foo struct { num int str string// ...}func New(num int, str string) *Foo {// ...return &Foo{ num: num, str: str, }}// ...
我们有一个Foo结构体,内部有num和str两个属性,New函数传入两个初始化参数,构造一个Foo对象。
ok,一切都足够简单。
假设我们需要对Foo内部增加两个属性,同时构造函数也需要支持传入这两个新增属性的初始值。有一种修改方法是这样的:
func New(num int, str string, num2 int, str2 string)
可以看到,这种方式,随着初始化参数个数、类型的变化,我们New函数的函数签名也需随之改变。这带来两个坏处:
- 对调用方来说,函数不兼容
- 参数数量太多,可读性可能变差
一
有一种保持兼容性的解决方案,是保留之前的New函数,再创建一个新的构造函数,比如New2,用于实现 4 个参数的构造方法。
这种解决方案在大部分时候会导致代码可维护性下降。
二
另一种解决方案,是把所有的参数都放入一个结构体中。就像这样:
type Foo struct { option Option// ...}type Option struct { num int str string}func New(option Option) *Foo {// ...retu