英文:
Equivalent of optional parameters in Go
问题
我正在将一个客户端从Python迁移到一个API。原始实现使用可选参数,如下所示:
def createIntAttribute(attr_name, default_value=None, is_array=False)
这将在数据库中创建一个整数属性,可以选择是否使用默认值,例如:
createIntAttribute("numUsers")             # 没有默认属性
createIntAttribute("numUsers", 0)          # 默认属性为0
在Go中如何实现这个功能呢?
我考虑了一些方法:
- 
使用int类型
无法确定用户是想避免创建默认值还是想创建默认值为0的属性。 - 
使用指针
defaultValue *int
需要在每次使用时加上&,并且不支持字面量。 - 
使用参数结构体
int的默认值是0,所以和第一种方法一样存在同样的问题。 - 
使用带有value和isSet属性的结构体封装可选参数
需要为每种类型的变量创建一个结构体。这可能可以使用泛型来实现,但要求Go版本大于1.18。使用方式为mypackage.NewOptionalStringArray([]string{"hello", "world"})。 - 
使用可变参数函数
func createIntAttribute(name string, optionals ...interface{})
默认情况下没有类型检查。自动完成不会显示变量的名称和类型;使用VS Code的单独参数会在我输入时显示参数的名称和类型。 
哪种方法是最好的实现方式呢?
英文:
I am porting a client to an API from python. The original implementation uses optional parameters as
def createIntAttribute(attr_name, default_value=None, is_array=False)
which create an integer attribute on database with or without a default value as in:
createIntAttribute("numUsers")             # No default attribute
createIntAttribute("numUsers", 0)          # Default attribute of 0
How can this be implemented in Go?
Some approaches I have considered are:
- Using int as type
There's no way to know if user wanted to avoid creating a default value or wanted to create a default of 0 - Using a pointer 
defaultValue *int
Needs&on every use and doesn't support literals - Using a struct for parameters
The default value for int is 0 so same problem as first approach - Using a struct with value and isSet attributes to enclose optional parameters
Needs a struct for each type of variable. This can probably use generics but adds a requirement of go version > 1.18
usage ismypackage.NewOptionalStringArray([]string{"hello", "world"}) - Using variadic function 
func createIntAttribute(name string, optionals ...interface{})
No typechecking by default
Autocomplete doesn't show name and type of variable; using individual argument with vs code shows the name of argument and its type as I am typing them 
What would be the best way to implement this?
答案1
得分: 4
一种方法是使用Functional Options Pattern。请参考下面的示例代码:
package main
import (
	"log"
)
type options struct {
	hasDefault   bool
	defaultValue int
}
type option func(*options)
func withDefault(value int) option {
	return func(o *options) {
		o.hasDefault = true
		o.defaultValue = value
	}
}
func createIntAttribute(name string, setters ...option) {
	o := &options{}
	for _, setter := range setters {
		setter(o)
	}
	log.Println(name, o)
}
func main() {
	createIntAttribute("test1")
	createIntAttribute("test1", withDefault(10))
}
这种方法可以更加用户友好(也许),当作为方法链实现时:
package main
import (
	"log"
)
type createIntAttributeParams struct {
	name         string
	hasDefault   bool
	defaultValue int
}
// 必需的参数在这里
func createIntAttribute(name string) *createIntAttributeParams {
	return &createIntAttributeParams{
		name: name,
	}
}
// 可选参数
func (p *createIntAttributeParams) withDefault(value int) *createIntAttributeParams {
	p.hasDefault = true
	p.defaultValue = value
	return p
}
// 其他 with* 函数用于设置更多可选参数
// 执行
func (p *createIntAttributeParams) do() {
	log.Println(*p)
}
func main() {
	createIntAttribute("test1").do()
	createIntAttribute("test1").withDefault(10).do()
}
英文:
One approach is to use the Functional Options Pattern. See the demo below:
package main
import (
	"log"
)
type options struct {
	hasDefault   bool
	defaultValue int
}
type option func(*options)
func withDefault(value int) option {
	return func(o *options) {
		o.hasDefault = true
		o.defaultValue = value
	}
}
func createIntAttribute(name string, setters ...option) {
	o := &options{}
	for _, setter := range setters {
		setter(o)
	}
	log.Println(name, o)
}
func main() {
	createIntAttribute("test1")
	createIntAttribute("test1", withDefault(10))
}
This approach can be more user friendly (maybe) when implemented as method chain:
package main
import (
	"log"
)
type createIntAttributeParams struct {
	name         string
	hasDefault   bool
	defaultValue int
}
// mandatory parameters here
func createIntAttribute(name string) *createIntAttributeParams {
	return &createIntAttributeParams{
		name: name,
	}
}
// optional parameter
func (p *createIntAttributeParams) withDefault(value int) *createIntAttributeParams {
	p.hasDefault = true
	p.defaultValue = value
	return p
}
// other with* functions to set more optional parameters
// execute
func (p *createIntAttributeParams) do() {
	log.Println(*p)
}
func main() {
	createIntAttribute("test1").do()
	createIntAttribute("test1").withDefault(10).do()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论