Go结构构造函数中具有默认值的可选参数

时间:2013-11-22 17:13:28

标签: constructor go default

我发现自己使用以下模式作为在Go结构构造函数中使用默认值获取可选参数的方法:

package main

import (
    "fmt"
)

type Object struct {
    Type int
    Name string
}

func NewObject(obj *Object) *Object {
    if obj == nil {
        obj = &Object{}
    }
    // Type has a default of 1
    if obj.Type == 0 {
        obj.Type = 1
    }
    return obj
}

func main() {
    // create object with Name="foo" and Type=1
    obj1 := NewObject(&Object{Name: "foo"})
    fmt.Println(obj1)

    // create object with Name="" and Type=1
    obj2 := NewObject(nil)
    fmt.Println(obj2)

    // create object with Name="bar" and Type=2
    obj3 := NewObject(&Object{Type: 2, Name: "foo"})
    fmt.Println(obj3)
}

是否有更好的方法允许使用默认值的可选参数?

5 个答案:

答案 0 :(得分:2)

这种方法对我来说似乎是合理的。但是,你有一个错误。如果我明确将Type设置为0,则会get switched to 1

我建议的修复:使用结构文字作为默认值:http://play.golang.org/p/KDNUauy6Ie

或者可能将其摘录出来:http://play.golang.org/p/QpY2Ymze3b

答案 1 :(得分:1)

您可以使用...运算符。

而不是像你写的python中那样编写ToCall(a = b),ToCall(“a”,b)

请参阅Go Play Example

func GetKwds(kwds []interface{}) map[string]interface{} {
    result := make(map[string]interface{})

    for i := 0; i < len(kwds); i += 2 {
        result[kwds[i].(string)] = kwds[i+1]
    }

    return result
}

func ToCall(kwds ...interface{}) {
    args := GetKwds(kwds)
    if value, ok := args["key"]; ok {
        fmt.Printf("key: %#v\n", value)
    }
    if value, ok := args["other"]; ok {
        fmt.Printf("other: %#v\n", value)
    }
}

func main() {
    ToCall()
    ToCall("other", &map[string]string{})
    ToCall("key", "Test", "other", &Object{})

}

答案 2 :(得分:1)

看看"Allocation with new" in Effective Go。他们解释了使零值结构成为一个有用的默认值。

如果您可以使Object.Type(以及您的其他字段)的默认值为零,那么Go struct literals已经为您提供了您正在请求的功能。

来自composite literals的部分:

  

复合文字的字段按顺序排列,并且必须全部存在。但是,通过将元素明确标记为字段:值对,初始值设定项可以按任何顺序显示,并将缺失的元素保留为各自的零值。

这意味着您可以替换它:

obj1 := NewObject(&Object{Name: "foo"})
obj2 := NewObject(nil)
obj3 := NewObject(&Object{Type: 2, Name: "foo"})

用这个:

obj1 := &Object{Name: "foo"}
obj2 := &Object{}
obj3 := &Object{Type: 2, Name: "foo"}

如果无法将零值设为所有字段的默认值,则为recommended approach is a constructor function。例如:

func NewObject(typ int, name string) *Object {
    return &Object{Type: typ, Name: name}
}

如果希望Type具有非零默认值,则可以添加另一个构造函数。假设Foo个对象是默认对象,并且Type为1。

func NewFooObject(name string) *Object {
    return &Object{Type: 1, Name: name}
}

您只需为您使用的每组非零默认值创建一个构造函数。您总是可以通过将某些字段的语义更改为默认值为零来减少该设置。

另外,请注意,使用零默认值向Object添加新字段不需要上面的任何代码更改,因为所有结构文字都使用标记初始化。这就派上用场了。

答案 3 :(得分:0)

https://play.golang.org/p/SABkY9dbCOD

这里是使用对象的方法设置默认值的替代方法。我发现它很有用,尽管它与您所使用的并没有太大区别。如果它是软件包的一部分,则可以更好地使用它。我并没有声称自己是围棋专家,也许您会提供一些额外的建议。

package main

import (
    "fmt"
)

type defaultObj struct {
    Name      string
    Zipcode   int
    Longitude float64
}

func (obj *defaultObj) populateObjDefaults() {
    if obj.Name == "" {
        obj.Name = "Named Default"
    }
    if obj.Zipcode == 0 {
        obj.Zipcode = 12345
    }
    if obj.Longitude == 0 {
        obj.Longitude = 987654321
    }
}

func main() {
    testdef := defaultObj{Name: "Mr. Fred"}
    testdef.populateObjDefaults()
    fmt.Println(testdef)

    testdef2 := defaultObj{Zipcode: 90210}
    testdef2.populateObjDefaults()
    fmt.Println(testdef2)

    testdef2.Name = "Mrs. Fred"
    fmt.Println(testdef2)

    testdef3 := defaultObj{}
    fmt.Println(testdef3)
    testdef3.populateObjDefaults()
    fmt.Println(testdef3)
}

输出:

{Mr. Fred 12345 9.87654321e+08}
{Named Default 90210 9.87654321e+08}
{Mrs. Fred 90210 9.87654321e+08}
{ 0 0}
{Named Default 12345 9.87654321e+08}

答案 4 :(得分:0)

Dave Cheney为此提供了一个不错的解决方案,您可以使用功能选项来覆盖默认值:

https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis

因此您的代码将变为:

package main

import (
    "fmt"
)

type Object struct {
    Type int
    Name string
}

func NewObject(options ...func(*Object)) *Object {
    // Setup object with defaults 
    obj := &Object{Type: 1}
    // Apply options if there are any
    for _, option := range options {
        option(obj)
    }
    return obj
}

func WithName(name string) func(*Object) {
    return func(obj *Object) {
        obj.Name = name
    }
}

func WithType(newType int) func(*Object) {
    return func(obj *Object) {
        obj.Type = newType
    }
}

func main() {
    // create object with Name="foo" and Type=1
    obj1 := NewObject(WithName("foo"))
    fmt.Println(obj1)

    // create object with Name="" and Type=1
    obj2 := NewObject()
    fmt.Println(obj2)

    // create object with Name="bar" and Type=2
    obj3 := NewObject(WithType(2), WithName("foo"))
    fmt.Println(obj3)
}

https://play.golang.org/p/pGi90d1eI52