Golang替代C++带有默认参数的函数:多个函数或结构参数。

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

Golang alternative to c++ function with default params: multiple functions, or struct param

问题

我想知道在Go语言中,与C++函数绑定默认参数的最佳实践是什么,这可能是对用户来说最容易看到函数参数的方式(通过linter的帮助)。你认为使用test函数的最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将显示test函数的所有可能性
  • 编译器将采用最短的路径

缺点:

  • 当参数很多时可能会变得复杂

2. 使用结构体参数:

type testOptions struct {
	X      int
	Y      int
	color  Color
}

func test(opt *testOptions)

// 用户调用
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指示参数列表)。

英文:

I would like to know the best practice in Go equivalent to C++ functions binding with default params, which may be easiest for the user to see the function params(with linter help).
What do you think will be the most GO style and easiest way to use the test function ?

An example function in C++:

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

Equivalence in Go

1. With multiple signatures:

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

pro:

  • The linter will show all the possibilities for test
  • The compiler will take the shortest path

cons:

  • Can be overwhelmed when there is a lots of params

2. With struct parameter:

type testOptions struct {
	X      int
	Y      int
	color  Color
}

func test(opt *testOptions)

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

pro:

  • Only one signature
  • Can specify only some values

cons:

  • Need to define a struct
  • The values will be set by default by the system

With the help of the module github.com/creasty/defaults, there is a way to set default values (but with the cost of calling reflect in runtime).

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)
	}
}

pro:

  • set default values

cons:

  • Use of reflect in runtime

P.S.:
I saw the use of variadic parameters ... or/with interface{} but I find it not easy to know which params to use (or maybe there is a way to indicate a params list to the linter).

答案1

得分: 5

无论哪种方式都可以正常工作,但在Go语言中,函数选项模式可能更符合实现此功能的习惯用法。

它基于接受可变数量的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
    }
}
英文:

Either way will work fine, but in Go the functional options pattern might be more idiomatic for implementing such functionality.

It is based on the idea of accepting a variable amount of WithXXX type of functional arguments that extend of modify the behavior of the call.

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
	}
}

答案2

得分: 3

我认为,如果结果只有几个函数,那么你的选项1是一个很好的惯用选择。我还认为函数选项模式是一个不错的选择,它经常在工厂函数中使用。

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

其中一个Go谚语是“让零值有用”,因此假设具有零值的类型意味着使用默认值。如果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

英文:

I think that your option 1 is a good idiomatic choice if the result is only a handful of functions. I also think the functional options pattern is a fine choice, it is often used in factory functions.

To add another choice, this question might be an indication that the code needs to be re-factored. Should test() be a method on a type which knows the optional parameters?

One of the go proverbs is "make the zero value useful", so a type with its zero value is assumed to mean use the default. If 0 is a valid value for your int type then consider *int, the point is to avoid referencing fields where you want to keep the default.

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

huangapple
  • 本文由 发表于 2021年9月21日 16:53:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/69266062.html
匿名

发表评论

匿名网友

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

确定