다음과 같은 속성을 가진 사용자 구조 개체 User를 코드에 정의한다고 가정합니다.
type User struct { ID string // 必需项 Name string // 必需项 Age int // 非必需项 Gender bool // 非必需项 }
객체를 초기화할 때 가장 간단한 방법은
u := &User{ID: "12glkui234d", Name: "菜刀", Age: 18, Gender: true}
와 같은 속성 값을 직접 입력하는 것입니다. 그러나 문제가 있습니다. 예를 들어 User 객체의 속성은 반드시 내보낼 수 있는 것은 아닙니다. 속성 필드는 비밀번호입니다(첫 번째 문자는 소문자이며, 다른 모듈에서 사용자 개체를 구성해야 하는 경우 비밀번호 필드를 채울 수 없습니다).
그래서 User 객체를 구성하는 함수를 정의해야 합니다. 생각할 수 있는 가장 간단한 생성자 메서드는 다음과 같습니다.
func NewUser(id, name string, age int, gender bool) *User { return &User{ ID: id, Name: name, Age: age, Gender: gender, } }
그러나 몇 가지 문제도 있습니다. User 개체의 경우 ID 및 Name 속성만 필요하고 Age 및 Gender는 선택 사항이며 기본값을 설정할 수 없습니다. 예를 들어 Age의 기본값은 0입니다. . Gender의 기본값은 false이며 이는 명백히 비합리적입니다.
이 문제에 직면했을 때 우리가 채택할 수 있는 해결책은 무엇입니까?
우리가 생각할 수 있는 가장 조잡한 해결책은 각 매개변수 상황에 대한 생성자를 설정하는 것입니다. 다음 코드와 같이
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 的代码(截图还未包括全部)。
기능적 옵션 패턴은 객체에 대한 매개변수를 동적이고 유연하게 구성하는 방법에 대한 문제를 해결하지만 적절한 시나리오에서 사용해야 합니다.
많은 선택적 매개변수, 가져오지 않은 필드, 매개변수가 버전에 따라 증가할 수 있는 등 개체의 구성 매개변수가 복잡한 경우 기능 옵션 모드가 매우 도움이 될 수 있습니다.
위 내용은 一个活跃在众多 Go 项目中的编程模式의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!