Equivalent of optional parameters in Go

huangapple go评论113阅读模式
英文:

Equivalent of optional parameters in Go

问题

我正在将一个客户端从Python迁移到一个API。原始实现使用可选参数,如下所示:

  1. def createIntAttribute(attr_name, default_value=None, is_array=False)

这将在数据库中创建一个整数属性,可以选择是否使用默认值,例如:

  1. createIntAttribute("numUsers") # 没有默认属性
  2. 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

  1. 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:

  1. createIntAttribute("numUsers") # No default attribute
  2. 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 is mypackage.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。请参考下面的示例代码:

  1. package main
  2. import (
  3. "log"
  4. )
  5. type options struct {
  6. hasDefault bool
  7. defaultValue int
  8. }
  9. type option func(*options)
  10. func withDefault(value int) option {
  11. return func(o *options) {
  12. o.hasDefault = true
  13. o.defaultValue = value
  14. }
  15. }
  16. func createIntAttribute(name string, setters ...option) {
  17. o := &options{}
  18. for _, setter := range setters {
  19. setter(o)
  20. }
  21. log.Println(name, o)
  22. }
  23. func main() {
  24. createIntAttribute("test1")
  25. createIntAttribute("test1", withDefault(10))
  26. }

这种方法可以更加用户友好(也许),当作为方法链实现时:

  1. package main
  2. import (
  3. "log"
  4. )
  5. type createIntAttributeParams struct {
  6. name string
  7. hasDefault bool
  8. defaultValue int
  9. }
  10. // 必需的参数在这里
  11. func createIntAttribute(name string) *createIntAttributeParams {
  12. return &createIntAttributeParams{
  13. name: name,
  14. }
  15. }
  16. // 可选参数
  17. func (p *createIntAttributeParams) withDefault(value int) *createIntAttributeParams {
  18. p.hasDefault = true
  19. p.defaultValue = value
  20. return p
  21. }
  22. // 其他 with* 函数用于设置更多可选参数
  23. // 执行
  24. func (p *createIntAttributeParams) do() {
  25. log.Println(*p)
  26. }
  27. func main() {
  28. createIntAttribute("test1").do()
  29. createIntAttribute("test1").withDefault(10).do()
  30. }
英文:

One approach is to use the Functional Options Pattern. See the demo below:

  1. package main
  2. import (
  3. "log"
  4. )
  5. type options struct {
  6. hasDefault bool
  7. defaultValue int
  8. }
  9. type option func(*options)
  10. func withDefault(value int) option {
  11. return func(o *options) {
  12. o.hasDefault = true
  13. o.defaultValue = value
  14. }
  15. }
  16. func createIntAttribute(name string, setters ...option) {
  17. o := &options{}
  18. for _, setter := range setters {
  19. setter(o)
  20. }
  21. log.Println(name, o)
  22. }
  23. func main() {
  24. createIntAttribute("test1")
  25. createIntAttribute("test1", withDefault(10))
  26. }

This approach can be more user friendly (maybe) when implemented as method chain:

  1. package main
  2. import (
  3. "log"
  4. )
  5. type createIntAttributeParams struct {
  6. name string
  7. hasDefault bool
  8. defaultValue int
  9. }
  10. // mandatory parameters here
  11. func createIntAttribute(name string) *createIntAttributeParams {
  12. return &createIntAttributeParams{
  13. name: name,
  14. }
  15. }
  16. // optional parameter
  17. func (p *createIntAttributeParams) withDefault(value int) *createIntAttributeParams {
  18. p.hasDefault = true
  19. p.defaultValue = value
  20. return p
  21. }
  22. // other with* functions to set more optional parameters
  23. // execute
  24. func (p *createIntAttributeParams) do() {
  25. log.Println(*p)
  26. }
  27. func main() {
  28. createIntAttribute("test1").do()
  29. createIntAttribute("test1").withDefault(10).do()
  30. }

huangapple
  • 本文由 发表于 2023年4月23日 09:50:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76082755.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定