Home  >  Article  >  Backend Development  >  A programming model active in many Go projects

A programming model active in many Go projects

Go语言进阶学习
Go语言进阶学习forward
2023-07-24 14:58:231291browse
Today we introduce a very popular programming model in the Go language: Functional Options. The problem solved by this pattern is how to configure parameters for objects more dynamically and flexibly. Maybe readers don’t quite understand this pain point, don’t worry, we will explain it in detail below.

Question

# Suppose we define a user structure object User in the code, which has the following properties .

type User struct {
 ID      string    // 必需项
 Name    string    // 必需项
 Age     int       // 非必需项
 Gender  bool      // 非必需项
}

When initializing the object, the simplest way is to directly fill in the attribute value, such as

u := &User{ID: "12glkui234d", Name: "菜刀", Age: 18, Gender: true}

But there is a problem here: User object The attributes in are not necessarily exportable. For example, User has an attribute field called password (the first letter is lowercase, not exported). If a User object needs to be constructed in other modules, the password field cannot be filled.

So we need to define a function to construct the User object. The simplest constructor method that can be thought of is as follows.

func NewUser(id, name string, age int, gender bool) *User {
 return &User{
  ID:     id,
  Name:   name,
  Age:    age,
  Gender: gender,
 }
}

But there are also some problems: for the User object, only the ID and Name properties are required, Age and Gender are non-required, and default values ​​cannot be set. For example, the default value of Age is 0, and the default value of Gender is false, which is obviously unreasonable.

Faced with this problem, what are the solutions we can adopt?

Option 1: Multi-function construction

The crudest solution we can think of is: set up a constructor for each parameter situation . As shown in the following code

func NewUser(id, name string) *User {
 return &User{ID: id, Name: name}
}

func NewUserWithAge(id, name string, age int) *User {
 return &User{ID: id, Name: name, Age: age}
}

func NewUserWithGender(id, name string, gender bool) *User {
 return &User{ID: id, Name: name, Gender: gender}
}

func NewUserWithAgeGender(id, name string, age int, gender bool) *User {
 return &User{ID: id, Name: name, Age: age, Gender: gender}
}

这种方式适合参数较少且不易发生变化的情况。该方式在 Go 标准库中也有使用,例如 net 包中的 Dial 和 DialTimeout 方法。

func Dial(network, address string) (Conn, error) {}
func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {}

但该方式的缺陷也很明显:试想,如果构造对象 User 增加了参数字段 Phone,那么我们需要新增多少个组合函数?

方案二:配置化

另外一种常见的方式是配置化,我们将所有可选的参数放入一个 Config 的配置结构体中。

type User struct {
 ID   string
 Name string
 Cfg  *Config
}

type Config struct {
 Age    int
 Gender bool
}

func NewUser(id, name string, cfg *Config) *User {
 return &User{ID: id, Name: name, Cfg: cfg}
}

这样,我们只需要一个 NewUser()  函数,不管之后增加多少配置选项,NewUser 函数都不会得到破坏。

但是,这种方式,我们需要先构造 Config 对象,这时候对 Config 的构造又回到了方案一中存在的问题。

方案三:函数式选项模式

面对这样的问题,我们还可以选择函数式选项模式。

首先,我们定义一个 Option 函数类型

type Option func(*User)

然后,为每个属性值定义一个返回 Option 函数的函数

func WithAge(age int) Option {
 return func(u *User) {
  u.Age = age
 }
}

func WithGender(gender bool) Option {
 return func(u *User) {
  u.Gender = gender
 }
}

此时,我们将 User 对象的构造函数改为如下所示

func NewUser(id, name string, options ...Option) *User {
 u := &User{ID: id, Name: name}
 for _, option := range options {
  option(u)
 }
 return u
}

按照这种构造方式,我们就可以这样配置 User 对象了

u := NewUser("12glkui234d", "菜刀", WithAge(18), WithGender(true))

以后不管 User 增加任何参数 XXX,我们只需要增加对应的 WithXXX 函数即可,是不是非常地优雅?

Functional Options 这种编程模式,我们经常能在各种项目中找到它的身影。例如,我在 tidb 项目中仅使用 opts ... 关键字搜索,就能看到这么多使用了 Functional Options 的代码(截图还未包括全部)。

A programming model active in many Go projects

Summary

The functional option pattern solves the problem of how to dynamically and flexibly configure parameters for objects, but it needs to be used in appropriate scenarios.

When the configuration parameters of the object are complex, such as many optional parameters, non-imported fields, parameters may increase with the version, etc., then the functional option mode can help very well. us.

The above is the detailed content of A programming model active in many Go projects. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:Go语言进阶学习. If there is any infringement, please contact admin@php.cn delete