web-dev-qa-db-fra.com

Constructeurs à Go

J'ai une structure et j'aimerais qu'elle soit initialisée avec des valeurs par défaut sensibles.

Généralement, la chose à faire est d'utiliser un constructeur, mais puisque go n'est pas vraiment OOP au sens traditionnel, ce ne sont pas de vrais objets et il n'a pas de constructeur.

J'ai remarqué la méthode init mais c'est au niveau du paquet. Y a-t-il autre chose de semblable qui peut être utilisé au niveau de la structure?

Si non, quelle est la meilleure pratique acceptée pour ce type de choses dans Go?

143
Marty Wallace

Il existe actuellement deux meilleures pratiques acceptées:

  1. Faites de la valeur zéro de votre structure une valeur raisonnable par défaut. (Bien que cela semble étrange à la plupart des gens venant de "traditionnel", cela fonctionne souvent et est vraiment pratique).
  2. Fournissez une fonction func New() YourTyp ou, si votre paquet contient plusieurs types de ce type, func NewYourType1() YourType1 et ainsi de suite.

Documentez si une valeur nulle de votre type est utilisable ou non (dans ce cas, elle doit être configurée par l'une des fonctions New.... (Pour les "traditionalistes" oups: Quelqu'un qui ne lit pas la documentation gagnée ' t être capable d’utiliser vos types correctement, même s’il ne peut pas créer d’objets dans des états non définis.)

115
Volker

Il existe des équivalents de constructeurs pour les cas où les valeurs zéro ne peuvent pas définir de valeurs par défaut raisonnables ou pour certains paramètres nécessaires à l'initialisation de la structure.

En supposant que vous ayez une structure comme celle-ci:

type Thing struct {
    Name  string
    Num   int
}

ensuite, si les valeurs zéro ne correspondent pas, vous construirez généralement une instance avec une fonction NewThing renvoyant un pointeur:

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

Lorsque votre structure est assez simple, vous pouvez utiliser cette construction condensée:

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

Si vous ne voulez pas renvoyer de pointeur, il est alors nécessaire d'appeler la fonction makeThing au lieu de NewThing:

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

Référence: Allocation avec nouveau dans Effective Go .

166
Denys Séguret

Allez a des objets. Les objets peuvent avoir des constructeurs (mais pas des constructeurs automatiques). Enfin, Go est un langage OOP (les types de données sont associés à des méthodes, mais il existe certes de nombreuses définitions de ce que OOP est.)

Néanmoins, la meilleure pratique acceptée consiste à écrire zéro ou plusieurs constructeurs pour vos types.

Comme @dystroy a posté sa réponse avant que je termine cette réponse, permettez-moi d'ajouter une version alternative de son exemple de constructeur, que j'écrirais probablement à la place:

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

La raison pour laquelle je veux vous montrer cette version est que très souvent, des littéraux "en ligne" peuvent être utilisés à la place d'un appel "constructeur".

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

Maintenant, *a == *b.

30
zzzz

Il n'y a pas de constructeurs par défaut dans Go, mais vous pouvez déclarer des méthodes pour n'importe quel type. Vous pourriez prendre l’habitude de déclarer une méthode appelée "Init". Je ne sais pas si cela est lié aux meilleures pratiques, mais cela aide à garder les noms courts sans perdre la clarté.

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)
}

Le résultat est:

Hello: 5
9
Sebastian Bartos

J'aime l'explication de ceci blog post :

La fonction New est une convention Go pour les packages qui créent un type de base ou différents types à utiliser par le développeur de l'application. Regardez comment New est défini et implémenté dans log.go, bufio.go et cypto.go:

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")
}

Étant donné que chaque package agit comme un espace de noms, chaque package peut avoir sa propre version de New. Dans bufio.go, vous pouvez créer plusieurs types, il n’existe donc pas de nouvelle fonction autonome. Ici vous trouverez des fonctions comme NewReader et NewWriter.

6
Ivan Aracki

une autre façon est;

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

Si vous voulez forcer l'utilisation de la fonction factory, nommez votre structure (votre classe) avec le premier caractère en minuscule. Ensuite, il ne sera pas possible d’instancier directement la structure, la méthode fabrique sera requise.

Cette visibilité basée sur les lettres majuscules/minuscules du premier caractère fonctionne également pour le champ struct et pour la fonction/méthode. Si vous ne souhaitez pas autoriser l'accès externe, utilisez des minuscules.

1

Golang n’est pas OOP dans ses documents officiels. Tous les champs de la structure de Golang ont une valeur déterminée (contrairement à c/c ++), la fonction constructeur n’est donc pas aussi nécessaire que cpp. Si vous devez affecter des valeurs spéciales à certains champs, utilisez les fonctions d'usine. La communauté de Golang suggère New .. noms de modèles.

0
hurricane1026