メインコンテンツにスキップ
モジュール 5 · レッスン 5 / 5コース内の 25/50~10 min
モジュールのレッスン (5/5)

`New...` コンストラクター パターン

Go には constructor キーワードがありません。コンベンションは工場です 適切に初期化された値またはポインタを返す関数。によって 名前は通常、NewT (または NewXxx) と呼ばれます。

ベースパターン:NewT

Go
type User struct {
    Name string
    Age  int
}

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

u := NewUser("Ada", 36)
fmt.Println(u.Name)

直接リテラル &User{...} よりも優れた利点:

  • 検証とデフォルトを一元化します。
  • エクスポートされていないフィールドがあり、コンストラクターからのみフィールドに値を設定できます。
  • 呼び出し元を中断せずに実装を変更できます。

検証付きコンストラクター: (*T, エラー)

初期化が失敗する可能性がある場合は、(*T, error) を返します。

Go
import "errors"

func NewUser(name string, age int) (*User, error) {
    if name == "" {
        return nil, errors.New("nome obbligatorio")
    }
    if age < 0 {
        return nil, errors.New("eta non valida")
    }
    return &User{Name: name, Age: age}, nil
}

u, err := NewUser("Ada", 36)
if err != nil {
    // handle...
}

これは、次のコンストラクターの慣用的なパターンです。 前提条件: データベース接続、ファイルハンドル、サーバー設定など。

複数のコンストラクター

構築する「方法」が複数ある場合は、わかりやすい名前を使用します。

Go
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }

Go ではオーバーロードはありません。名前は曖昧さを解消します。

機能オプション (高度な概要)

オプションのパラメーターが多数ある場合、慣用的なパターンは次のとおりです。 「機能オプション」:

Go
type Option func(*Server)

func WithPort(p int) Option {
    return func(s *Server) { s.Port = p }
}

func WithTLS(cert, key string) Option {
    return func(s *Server) { s.Cert, s.Key = cert, key }
}

func NewServer(opts ...Option) *Server {
    s := &Server{Port: 8080} // default
    for _, opt := range opts {
        opt(s)
    }
    return s
}

s := NewServer(WithPort(9000), WithTLS("c.pem", "k.pem"))

リテラルよりも冗長ですが、多くのオプションで適切に拡張できます。 パラメータを削除し、下位互換性を維持します (新しい Option を追加します) 既存の呼び出し元を中断しません)。

コンストラクターが必要ない場合

型のゼロ値がすでに有効である場合は、呼び出し元に 直接リテラル。正規の例: sync.Mutex にはコンストラクターがありません。 var m sync.Mutex はすでにすぐに使用できるミューテックスです。 bytes.Buffer についても同様です。

ルールは「ゼロの値を有用なものにする」です。できるなら、いいえ 必須のコンストラクター。

試してみてください

運動#go.m5.l5.e1
試行回数: 0読み込み中…

&User{Name: name, Age: age} を返すファクトリー関数 NewUser(name string, age int) *User を記述します。

エディターを読み込み中…
ヒントを表示

ポインタを返すには、リテラル `&User{...}` を使用します。

3 回の試行後に解決策が利用可能になります

運動#go.m5.l5.e2
試行回数: 0読み込み中…

NewUser を (name string) に変換します (*User, error): name == '' の場合、nil、errors.New('nome obbligatorio') が返されます。

エディターを読み込み中…
ヒントを表示

前提条件がある場合は (*T, error) を返し、失敗した場合は nil + エラーを返します。

3 回の試行後に解決策が利用可能になります

Quiz#go.m5.l5.e3
準備完了

Go におけるコンストラクターの慣用的な慣例は何ですか?

Go
// Which signature is more idiomatic?
回答の選択肢

要約

  • constructor なし: NewT 関数 (または、同じ名前のパッケージ内の New) を使用します。
  • 単純な場合は *T を返します。 (*T, error) 前提条件または検証付き。
  • エラー時: nil をポインターとして返し、説明エラーを 2 番目の値として返します。
  • 過負荷はありません。わかりやすい名前 (NewUserFromJSON、NewAdmin) を使用してください。
  • 「ゼロ値を便利にする」: 可能であれば、コンストラクターは不要です。
  • 多くのオプションパラメータの高度なパターン: 機能オプション。