【问题标题】:Golang alternative to c++ function with default params: multiple functions, or struct paramGolang 替代具有默认参数的 c++ 函数:多个函数或结构参数
【发布时间】:2021-11-14 20:22:04
【问题描述】:

我想知道 Go 中等效于使用默认参数绑定的 C++ 函数的最佳实践,这对于用户来说可能最容易看到函数参数(在 linter 的帮助下)。
您认为最 GO 风格和最简单的测试功能使用方式是什么?

C++ 中的示例函数:

void test(int x, int y=0, color=Color());

Go 中的等价性

1.具有多个签名:

func test(x int)
func testWithY(x int, y int)
func testWithColor(x int, color Color)
func testWithYColor(x int, y int, color Color)

专业人士:

  • linter 将显示测试的所有可能性
  • 编译器将采用最短路径

缺点:

  • 当有很多参数时可能会不堪重负

2。带结构参数:

type testOptions struct {
    X      int
    Y      int
    color  Color
}

func test(opt *testOptions)

// user 
test(&testOptions{x: 5})

专业人士:

  • 只有一个签名
  • 只能指定一些值

缺点:

  • 需要定义一个结构体
  • 这些值将由系统默认设置

借助模块github.com/creasty/defaults,可以设置默认值(但需要在运行时调用反射)。

type testOptions struct {
    X      int
    Y      int `default:"10"`
    color  Color `default:"{}"`
}

func test(opt *testOptions) *hg.Node {
    if err := defaults.Set(opt); err != nil {
        panic(err)
    }
}

专业人士:

  • 设置默认值

缺点:

  • 在运行时使用反射

附: 我看到了使用可变参数... 或/与interface{},但我发现要知道使用哪些参数并不容易(或者也许有一种方法可以向 linter 指示参数列表)。

【问题讨论】:

标签: c++ go idioms function-parameter


【解决方案1】:

无论哪种方式都可以正常工作,但在 Go 中,functional options pattern 可能更适合实现此类功能。

它基于接受可变数量的 WithXXX 类型的函数参数的想法,这些参数扩展了修改调用的行为。

type Test struct {
    X     int
    Y     int
    color Color
}

type TestOption func(*Test)

func test(x int, opts ...TestOption) {
    p := &Test{
        X: x,
        Y: 12,
        Color: defaultColor,
    }
    for _, opt := range opts {
        opt(p)
    }
    p.runTest()
}

func main() {
    test(12)
    test(12, WithY(34))
    test(12, WithY(34), WithColor(Color{1, 2, 3}))
}

func WithY(y int) TestOption {
    return func(p *Test) {
        p.Y = y
    }
}

func WithColor(c Color) TestOption {
    return func(p *Test) {
        p.color = c
    }
}

【讨论】:

  • 感谢您的回答,这个想法很好地构建了一个复杂的对象。我的目标是提供一个非常简单的 api 来调用具有默认参数的绑定函数。对于新手来说,第一个选项似乎最容易理解,但我记住你的选项对于默认参数和对象构建非常好。问题:如何告诉 linter WithY 可以用于 test ? (为了最简单的使用)
  • “功能选项模式”是可用的,但随着时间的推移,它的缺陷变得明显——它需要维护大量的样板,以换取在可用性方面基本上没有任何收益。 (例如,如果您传递两个不同的 WithY 选项或两个不同的 WithColor 选项会发生什么?)新的 API 倾向于选择(哈!) struct 参数是有原因的。
【解决方案2】:

如果结果只有少数几个函数,我认为您的选项 1 是一个很好的惯用选择。我也觉得函数式选项模式是个不错的选择,它经常用在工厂函数中。

要添加另一个选择,这个问题可能表明代码需要重构。 test() 应该是知道可选参数的类型的方法吗?

go proverbs 之一是“使零值有用”,因此假定具有零值的类型意味着使用默认值。如果0 是您的int 类型的有效值,那么请考虑*int,关键是要避免引用您希望保留默认值的字段。

package main

import (
    "fmt"
)

const (
    defaultY     = 10
    defaultColor = "blue"
)

type Color string

type Thing struct {
    Y     int
    Color Color
}

func (t Thing) getY() int {
    if t.Y == 0 {
        return defaultY
    }
    return t.Y
}

func (t Thing) getColor() Color {
    if t.Color == "" {
        return defaultColor
    }
    return t.Color
}

func (t Thing) Test(x int) {
    fmt.Println(x, t.getY(), t.getColor())
}

func main() {
    Thing{}.Test(12)
    Thing{Y: 11}.Test(12)
    Thing{Y: 11, Color: "red"}.Test(12)
}

// 12 10 blue
// 12 11 blue
// 12 11 red

【讨论】:

  • 感谢您的回答,此选项非常适合构建对象。 linter 将显示可用的不同功能。
猜你喜欢
  • 1970-01-01
  • 2012-11-03
  • 2017-09-27
  • 1970-01-01
  • 2018-04-08
  • 2014-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多