Golang动态可变函数参数 参数默认值

作者:matrix 发布时间:2024-08-17 分类:Golang

Golang是不支持函数参数默认值的,但是也有很多办法可以解决

动态可变参数

func main() {
    addItem("11", "a1")
    addItem("2", "a2", "222")
}

func addItem(name, value string, opts ...string) {
    fmt.Println("add item-->", name, value)
    for _, opt := range opts {
        fmt.Println("opt:", opt)
    }
}

其中opts ...string 表示可变参数,类型为string,如果需要不同类型传入 看下面

可变参数 + 动态类型

type AlfredItem struct {
    Title    string
    Subtitle string
    Arg      int
}

type AlfredWorkflow struct {
    Items []AlfredItem
}

func (aw *AlfredWorkflow) AddItem(name, value string, opts ...func(*AlfredItem)) {
    item := AlfredItem{
        Title:    value,
        Subtitle: name,
        Arg:      111,
    }
    for _, opt := range opts {
        opt(&item)
    }
    aw.Items = append(aw.Items, item)
}

func main() {
    aw := AlfredWorkflow{}
    aw.AddItem("A", "a")
    aw.AddItem("B", "b", func(ai *AlfredItem) {
        ai.Arg = 22222
    }, func(ai *AlfredItem) {
        ai.Arg = 3333
    })

    fmt.Printf("%+v", aw)
}

高阶用法 封装为选项模式(Option Pattern)

采用Functional Options Patter方法来解决

核心点:定义 type func(*AlfredItem),且每个参数定义with函数

type AlfredItem struct {
    Title    string
    Subtitle string
    Arg      int
}

type AlfredWorkflow struct {
    Items []AlfredItem
}

type Option func(*AlfredItem)

func WithTitle(title string) Option {
    return func(ai *AlfredItem) {
        ai.Title = title
    }
}
func WithSubtitle(subtitle string) Option {
    return func(ai *AlfredItem) {
        ai.Subtitle = subtitle
    }
}

func WithArg(arg int) Option {
    return func(ai *AlfredItem) {
        ai.Arg = arg
    }
}

func (aw *AlfredWorkflow) AddItem(name, value string, opts ...Option) {
    item := AlfredItem{
        Title:    value,
        Subtitle: name,
        Arg:      111,
    }
    for _, opt := range opts {
        opt(&item)
    }
    aw.Items = append(aw.Items, item)
}

func main() {
    aw := AlfredWorkflow{}
    aw.AddItem("DefaultName", "DefaultVlaue")
    aw.AddItem("DefaultName-B", "DefaultVlaue-b", WithArg(222), WithSubtitle("0000"))
    aw.AddItem("C", "c", WithTitle("hahah"))

    fmt.Printf("%+v", aw)
}

参考:

https://www.cnblogs.com/smartrui/p/10324320.html

go generate 为枚举类型生成字符串描述方法

作者:matrix 发布时间:2024-08-10 分类:Golang

go generate命令可以方便的为自动生成源代码,利用官方的stringer库来完成

安装stringer工具

如果本地已经安装,跳过

 go get -u golang.org/x/tools/cmd/stringer  

Case

main.GO

package main

import "fmt"

type UserStatus int
const (
    Active   UserStatus = 40
    Inactive UserStatus = 1
    Pending  UserStatus = 9
    Other               = Inactive
)

上面定义的常量类型UserStatus,原始类型为 int 值,每次使用 fmt.Print打印会只显示数字,可读性会很差。

那怎么让fmt.Print输出对应的描述?

自定义结构体String() 方法,打印时会自动调用

...
func (s UserStatus) String() string {
    switch s {
    case Active:
        return "Active"
    case Inactive:
        return "Inactive"
    case Pending:
        return "Pending"
    default:
        return "Other"
    }
}

func main(){
    var a UserStatus = Active
    fmt.Println(a) //Active
}

定义go:generate

上面手动编写的确可以,但如果有状态值调整后续维护会很麻烦,结合go:generate能自动生成String()方法

定义特定开头规则的注释//go:generate,这样go generate可以自动识别

//go:generate go run golang.org/x/tools/cmd/stringer -type=UserStatus
type UserStatus int

说明:
go:generate 表示go generate命令标记

go run golang.org/x/tools/cmd/stringer 表示stringer的执行命令,如果本地已经全局安装了其实也可以替换为stringer。但你得确保环境变量能够读取到它

-type 参数用于指定自定义的类型UserStatus

执行go:generate

go generate main.go 

不指定main.go 文件,generate命令会查找所有包含 //go:generate 指令的文件,并执行这些指令后面的命令。这个例子就会运行 stringer -type=UserStatus,为 UserStatus 类型生成一个新的 Go 文件userstatus_string.go,包含 String() 方法的实现。

自动生成的userstatus_string.go 文件

// Code generated by "stringer -type=UserStatus"; DO NOT EDIT.

package main

import "strconv"

func _() {
    // An "invalid array index" compiler error signifies that the constant values have changed.
    // Re-run the stringer command to generate them again.
    var x [1]struct{}
    _ = x[Active-40]
    _ = x[Inactive-1]
    _ = x[Pending-9]
}

const (
    _UserStatus_name_0 = "Inactive"
    _UserStatus_name_1 = "Pending"
    _UserStatus_name_2 = "Active"
)

func (i UserStatus) String() string {
    switch {
    case i == 1:
        return _UserStatus_name_0
    case i == 9:
        return _UserStatus_name_1
    case i == 40:
        return _UserStatus_name_2
    default:
        return "UserStatus(" + strconv.FormatInt(int64(i), 10) + ")"
    }
}

自动生成的代码中String()其实都大同小异,但是他考虑到了其他值。
并且_()匿名的函数内置逻辑用例可以起到防止枚举值被修改的问题,比如这里Active值被调整后会导致x[Active-40]取到非下标值导致编译失败 So Nice~
并且标注了DO NOT EDIT.

这样以后维护和构建过程更简单明了。

参考:

https://medium.com/@dadcod/6-unique-and-lesser-known-go-techniques-9821be24972b

https://www.jvt.me/posts/2022/06/15/go-tools-dependency-management/

stringer 源码:
https://cs.opensource.google/go/x/tools/+/master:cmd/stringer/stringer.go