ホームページ  >  記事  >  バックエンド開発  >  多くの Go プロジェクトで活躍するプログラミング モデル

多くの Go プロジェクトで活躍するプログラミング モデル

Go语言进阶学习
Go语言进阶学习転載
2023-07-24 14:58:231343ブラウズ
今日は、Go 言語で非常に人気のあるプログラミング モデルである関数オプションを紹介します。このパターンで解決される問題は、オブジェクトのパラメータをより動的かつ柔軟に設定する方法です。もしかしたら読者はこの問題点をよく理解していないかもしれませんが、心配しないでください。以下で詳しく説明します。

#質問

# コード内で次のプロパティを持つユーザー構造オブジェクト User を定義するとします。

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

オブジェクトを初期化するとき、最も簡単な方法は、

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

などの属性値を直接入力することですが、問題があります。ここ: ユーザー オブジェクト の属性は必ずしもエクスポート可能ではありません。たとえば、ユーザーにはパスワードという属性フィールドがあります (最初の文字は小文字でエクスポートされません)。ユーザー オブジェクトを他のモジュールで構築する必要がある場合、パスワード フィールドはエクスポートできません。満たされました。

したがって、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 ですが、これは明らかに不合理です。

この問題に直面した場合、私たちが採用できる解決策は何でしょうか?

オプション 1: 多機能の構築

考えられる最も大雑把な解決策は、各パラメーターの状況にコンストラクターをセットアップすることです。次のコードに示すように

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 プロジェクトで活躍するプログラミング モデル

概要

関数オプション パターンは、オブジェクトのパラメーターを動的かつ柔軟に構成する方法の問題を解決しますが、適切なシナリオで使用する必要があります。

オブジェクトの構成パラメータが複雑である場合 (多くのオプション パラメータ、インポートされていないフィールド、バージョンとともにパラメータが増加する可能性があるなど)、関数オプション モードが役立ちます。そうですね、私たち。

以上が多くの Go プロジェクトで活躍するプログラミング モデルの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はGo语言进阶学习で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。