Golang函数选项模式

20 篇文章 4 订阅

由于Golang不支持函数重载,因此无法像C++或Java那样可以定义多个同名不同参的构造函数,因此如果要定义多个构造函数就需要使用不同的名称。但是如果后续要给结构体增加新的字段,那么可能就需要修改很多的构造函数,函数选项模式就可以完美解决这个问题。

1、正常的构造函数

拿下面的代码举个例子:

type User struct {
    username string
    password string
    nickname string
    age uint8
    gender bool
    address string
    email string
}

func NewUser(username, password, nickname, address, email string, age uint8, gender bool) *User {
    return &User {
        username: username,
        ...
    }
}

// 假设有时候只想填username和password,如果使用上一个构造函数就需要这样写:
u := NewUser("zhangsan", "fawaikuangtu", "", "", "", 0, false)
// 非常的麻烦

// 这时候可以新增一个构造函数
func NewUserSimple(username, password string) *User {
    return &User {
        username: username,
        password: password,
    }
}
// 如果有别的需要可能要增加更多的构造函数,也是挺麻烦的
// 当我们在开发过程中,需要给User新增一个子段,假设为手机号,那么我们就需要修改User结构体,以及其一些构造函数,如果构造函数比较多,就可能需要修改大量的构造函数

// 也许你想到可以给其添加getter和setter:
type User struct {
    username string
    password string
    nickname string
    age uint8
    gender bool
    address string
    email string
	phoneNum string
}

func (u *User) GetPhoneNum() string {
    return u.phoneNum
}

func (u *User) SetPhoneNum(pnum string) {
    u.phoneNum = pnum
}

// 然后在创建对象的时候可以这样
u := NewUser("zhangsan", "fawaikuangtu", "", "", "", 0, false)
u.SetPhoneNum("911")

// 是不是看起来挺奇葩的,其他的参数都可以通过构造函数来设置,而phoneNum要单独使用setter来设置
// 这样不符合go语言的简洁性,那还不如直接将电话号码首字母大写设为可导出的直接赋值来的快:
u := NewUser("zhangsan", "fawaikuangtu", "", "", "", 0, false)
u.PhoneNum = "911"
// 而且Go语言不像其他语言,对于getter和setter使用的很少。我感觉使用这个也是多此一举

2、函数选项模式

接下来使用函数选项模式来实现构造函数:

type User struct {
    username string
    password string
    nickname string
    age uint8
    gender bool
    address string
    email string
}

type OptionFunc func(u *User)

// 假设username和password是必须要填的选项,那么构造函数就可以这样写
func NewUser(username, password string, options ...OptionFunc) *User {
    u := &User{
        username: username,
        password: password,
    }
    
    for _, o := range options {
        o(u)
    }
    
    // 也可以在这设置一些默认值
    if u.age == 0 {
        u.age = 18
    }
    
    ...
    
    return u
}

// 然后在下面提供一系列的设置值的With函数
func WithNickname(nickname string) OptionFunc {
    return func(u *User) {
        u.nickname = nickname
    }
}

func WithAge(age uint8) OptionFunc {
    return func(u *User) {
        u.age = age
    }
}

...

func WithEmail(email string) OptionFunc {
    return func(u *User) {
        u.email = email
    }
}

// 在创建对象的时候就可以这样写了
u := NewUser("zhangsan", "fawaikuangtu", WithNickname("luoxiang"), WithEmail("kuangtu@qq.com"))

// 当我们新增加了一个手机号的字段时,只需要在结构体中新增字段以及新增一个WithPhoneNum的函数即可

当然,也有一些其他的方式,比如下面的方式:

type User struct {
    username string
    password string
    nickname string
    age uint8
    gender bool
    address string
    email string
}

func NewUser(username, password string) *User {
    return &User{
        username: username,
        password: password,
    }
}

func (u *User) Nickname(nickname string) *User {
    u.nickname = nickname
    return u
}

...

func (u *User) Address(addr string) *User {
    u.address = addr
    return u
}

func (u *User) Email(email string) *User {
    u.email = email
    return u
}

// 在创建对象时这样来
u := NewUser("zhangsan", "fawaikuangtu").Nickname("luoxiang").Email("kuangtu@qq.com")

// 这种方式跟函数选项模式有点相似,不同的是这个方式需要给对应的结构体绑定方法,这也是个不太好的地方。

总结:函数选项模式在一些场景下可以很好地解决golang不支持函数重载的问题,但是在一些比较简单的场景下,比如只有两三个甚至一个构造函数的情况下,完全没有必要使用函数选项模式,直接写不同名的构造函数即可。但是在参数复杂同时一些参数又会有默认值的情况下,就可以使用函数选项模式,在很多的开源库中,函数选项模式也有使用,在大多数场景下用于一些基础的配置,比如Mysql、Redis等。

函数选项模式的优点

  • 支持任意顺序的参数传递
  • 易于扩展,新增字段时,只需新增一个函数即可
  • 支持默认值的设置

缺点

  • 需要很多选项函数,增大代码量
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值