web-dev-qa-db-ja.com

Goのコンストラクター

構造体があり、適切なデフォルト値を使用して初期化する必要があります。

通常、ここで行うことはコンストラクタを使用することですが、goは実際にはOOPではないため、従来の意味ではこれらは真のオブジェクトではなく、コンストラクタはありません。

Initメソッドに気付きましたが、それはパッケージレベルです。構造体レベルで使用できる類似のものは他にありますか?

そうでない場合は、Goでこのタイプのことについて受け入れられているベストプラクティスは何ですか?

143
Marty Wallace

実際には、2つの受け入れられたベストプラクティスがあります。

  1. 構造体のゼロ値を適切なデフォルトにします。 (これは「伝統的な」OOPから来ているほとんどの人にとっては奇妙に見えますが、しばしば機能し、本当に便利です)。
  2. 関数func New() YourTypを提供するか、パッケージ関数にそのような型がいくつかある場合はfunc NewYourType1() YourType1など。

タイプのゼロ値が使用可能かどうかを記録します(その場合は、New...関数のいずれかで設定する必要があります(「伝統主義者」の場合:ドキュメントを読んでいない人は '未定義の状態でオブジェクトを作成できない場合でも、型を適切に使用できるようにする。)

115
Volker

ゼロ値で適切なデフォルト値を作成できない場合や、構造体の初期化にパラメーターが必要な場合には、コンストラクターに相当するものがいくつかあります。

次のような構造体があるとします。

type Thing struct {
    Name  string
    Num   int
}

次に、ゼロの値が適切でない場合、通常、ポインターを返すNewThing関数を使用してインスタンスを作成します。

func NewThing(someParameter string) *Thing {
    p := new(Thing)
    p.Name = someParameter
    p.Num = 33 // <- a very sensible default value
    return p
}

構造体が非常に単純な場合、この要約構造体を使用できます。

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33}
}

ポインターを返したくない場合は、makeThingの代わりに関数NewThingを呼び出すことをお勧めします。

func makeThing(name string) Thing {
    return Thing{name, 33}
}

参照: Effective Goの新しい割り当て

166
Denys Séguret

Goにはオブジェクトがあります。オブジェクトにはコンストラクターを使用できます(ただし、自動コンストラクターは使用できません)。最後に、GoはOOP言語です(データ型にはメソッドが添付されていますが、OOPとは無限に定義されていることは確かです)。

それでも、受け入れられているベストプラクティスは、型に対して0個以上のコンストラクタを記述することです。

この答えを終える前に@dystroyが彼の答えを投稿したので、彼の例のコンストラクタの代替バージョンを追加してみましょう。

func NewThing(someParameter string) *Thing {
    return &Thing{someParameter, 33} // <- 33: a very sensible default value
}

このバージョンをお見せしたいのは、「コンストラクタ」呼び出しの代わりに「インライン」リテラルを使用できることが多いためです。

a := NewThing("foo")
b := &Thing{"foo", 33}

*a == *b

30
zzzz

Goにはデフォルトのコンストラクタはありませんが、任意の型のメソッドを宣言できます。 「Init」というメソッドを宣言するのを習慣にすることができます。これがベストプラクティスにどのように関係するかはわかりませんが、わかりやすくすることなく名前を短くするのに役立ちます。

package main

import "fmt"

type Thing struct {
    Name string
    Num int
}

func (t *Thing) Init(name string, num int) {
    t.Name = name
    t.Num = num
}

func main() {
    t := new(Thing)
    t.Init("Hello", 5)
    fmt.Printf("%s: %d\n", t.Name, t.Num)
}

結果は次のとおりです。

Hello: 5
9

私はこれからの説明が好きです ブログ投稿

関数Newは、アプリケーション開発者が使用するコアタイプまたは異なるタイプを作成するパッケージのGo規約です。 log.go、bufio.go、cypto.goでNewがどのように定義および実装されているかを見てください。

log.go

// New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) * Logger {
    return &Logger{out: out, prefix: prefix, flag: flag}
}

bufio.go

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) * Reader {
    return NewReaderSize(rd, defaultBufSize)
}

crypto.go

// New returns a new hash.Hash calculating the given hash function. New panics
// if the hash function is not linked into the binary.
func (h Hash) New() hash.Hash {
    if h > 0 && h < maxHash {
        f := hashes[h]
        if f != nil {
            return f()
        }
    }
    panic("crypto: requested hash function is unavailable")
}

各パッケージは名前空間として機能するため、すべてのパッケージは独自のバージョンのNewを持つことができます。 bufio.goでは複数のタイプを作成できるため、スタンドアロンの新しい関数はありません。ここには、NewReaderやNewWriterなどの関数があります。

6
Ivan Aracki

別の方法は次のとおりです。

package person

type Person struct {
    Name string
    Old  int
}

func New(name string, old int) *Person {
    // set only specific field value with field key
    return &Person{
        Name: name,
    }
}
3
K-Gun

ファクトリー関数の使用を強制する場合は、小文字の最初の文字で構造体(クラス)に名前を付けます。次に、構造体を直接インスタンス化することはできません、ファクトリメソッドが必要になります。

最初の文字の小文字/大文字に基づくこの可視性は、構造体フィールドおよび関数/メソッドでも機能します。外部アクセスを許可したくない場合は、小文字を使用してください。

1

Golangは公式ドキュメントではOOP言語ではありません。 Golang構造体のすべてのフィールドには(c/c ++とは異なり)決定された値があるため、コンストラクター関数はcppほど必要ではありません。一部のフィールドに特別な値を割り当てる必要がある場合は、ファクトリ関数を使用します。 Golangのコミュニティは、新しいパターン名を提案しています。

0
hurricane1026