• 前言

    ​ 在Go语言中,是不支持类似Python的可选参数,在函数进行参数拓展或者配置参数的默认参数就比较麻烦。如果使用不当,会在将来接口拓展中造成很大的影响。

    主要影响:

    1. 破坏兼容性

      直接在函数定义处,添加拓展参数,会导致所以调用处都要进行修改,破坏了代码的完整性,因为有的逻辑中可能不需要该参数。

    2. 不破坏兼容性,重新添加一个新的初始化的函数,随着参数添加的越来越多,初始化函数越来越多,非常冗余。

    ​ 因此使用可选参数配置的方式进行初始化和默认参数的设置就优雅的解决了参数拓展带来的一系列问题。

  • 实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    package main

    import "fmt"

    // 本文是实现可选参数的最佳实现
    // 对于函数的可选参数和默认参数配置,通常要在不影响不破坏现有逻辑基础上进行参数的添加
    // 有一种优秀的操作是通过options选项,使用函数进行参数的初始化和可选值的设置
    // 如下数据库的配置,我们希望可以在初始化时,使用默认的参数,也可以在测试时进行自己对可选参数的修改,不用全部输入全部数据
    type DataBaseConfig struct {
    IP string
    Port string
    Db int
    UserName string
    PassWord string
    }

    const (
    PassWord = "asd@123"
    UserName = "test"
    Db = 1
    )

    type DatabaseOptions func(*DataBaseConfig)

    // 添加数据库DB
    func WithDb(db int) DatabaseOptions {
    return func(con *DataBaseConfig) {
    con.Db = db
    }
    }

    // 添加用户名和密码
    func WithUserNamePassWord(userName, passWord string) DatabaseOptions {
    return func(con *DataBaseConfig) {
    con.UserName = userName
    con.PassWord = passWord
    }
    }

    // 可以设置默认参数
    func DefaultDatabaseConfig(con *DataBaseConfig) *DataBaseConfig {
    con.PassWord = PassWord
    con.UserName = UserName
    con.Db = Db
    return con
    }


    func NewDataBaseConnect(IP, Port string, options ...DatabaseOptions) *DataBaseConfig {
    // 本初始化方案,要自己填IP,其他参数可填可不填,也可以使用默认参数
    con := &DataBaseConfig{
    IP: IP,
    Port: Port,
    }
    // 默认值的设定
    con = DefaultDatabaseConfig(con)

    // 遍历可选参数,然后分别调用匿名函数,将连接对象指针传入,进行修改
    for _, op := range options {
    // 遍历调用函数,进行数据修改
    op(con)
    }
    return con
    }

    func main() {
    // 传入自定义参数测试可选参数的输入
    var options = []DatabaseOptions {
    WithDb(2),
    WithUserNamePassWord("hello", "passWord"),
    }
    con1 := NewDataBaseConnect("127.0.0.1", "27017", options...)
    fmt.Printf("%#v", con1)

    // 不输入可选参数的测试
    con := NewDataBaseConnect("127.0.0.1", "27017")
    fmt.Printf("%#v", con)
    }
  • 结果

    1
    2
    3
    &main.DataBaseConfig{IP:"127.0.0.1", Port:"27017", Db:2, UserName:"hello", PassWord:"passWord"}

    &main.DataBaseConfig{IP:"127.0.0.1", Port:"27017", Db:1, UserName:"test", PassWord:"asd@123"}
  • 说明

    ​ 本实现方式,是通过一个options的数组,进行解包,数组中传递就是自定义可选参数的函数,通过在初始化的函数中,进行循环遍历,然后调用给定可选参数对应的函数,进行返回对象的修改。如果以后需要拓展,只要在目标结构体中添加字段,然后添加With字段函数即即可,非常简单实用。在源码包中就有很多的实现案例。本文只是简单赘述。