Leçons du module (5/5)
Le motif de constructeur `New...`
Go n'a pas constructor comme mot-clé. La convention est une
fonction d'usine qui renvoie correctement une valeur ou un pointeur
initialisé. De par son nom, il est généralement appelé NewT (ou Newxxx).
Modèle de base : NewT
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)Avantages par rapport au littéral direct &User{...} :
- Centraliser la validation et les valeurs par défaut.
- Vous pouvez avoir des champs non exportés et les renseigner uniquement depuis le constructeur.
- Changez l'implémentation sans interrompre les appelants.
Constructeur avec validation : (*T, erreur)
Lorsque l'initialisation peut échouer, renvoyez (*T, error) :
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 {
// gestisci...
}C'est le modèle idiomatique pour tout constructeur avec conditions préalables : connexion à la base de données, descripteur de fichier, configuration du serveur, etc.
Plusieurs constructeurs
Si vous disposez de plusieurs « façons » de construire, utilisez des noms descriptifs :
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }Pas de surcharge dans Go : le nom sort du lot.
Options fonctionnelles (note avancée)
Lorsque les paramètres sont nombreux et facultatifs, le modèle idiomatique est "options fonctionnelles":
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"))Il est plus verbeux qu'un littéral mais s'adapte bien à de nombreux paramètres
options et maintient la compatibilité ascendante (ajoutez un nouveau Option
ne coupe pas les appelants existants).
Quand vous n'avez PAS besoin d'un constructeur
Si la valeur zéro du type est déjà utile, laissez l'appelant utiliser le
littéral direct. Exemple canonique : sync.Mutex n'a pas de constructeur,
var m sync.Mutex est déjà un mutex prêt. Idem pour bytes.Buffer.
La règle s'applique : « Rendre la valeur zéro utile ». Si tu réussis, rien constructeur obligatoire.
Essayez-le vous-même
Écrivez la fonction d'usine NewUser(name string, age int) *User qui renvoie &User{Name: name, Age: age}.
Afficher l'indice
Utilisez le littéral `&User{...}` pour renvoyer un pointeur.
Solution disponible après 3 tentatives
Transformez NewUser en (chaîne de nom) (*Utilisateur, erreur) : si nom == '' renvoie nul, erreurs.Nouveau('nom requis').
Afficher l'indice
S'il y a des conditions préalables, renvoyez (*T, erreur) et renvoyez nil + erreur en cas d'échec.
Solution disponible après 3 tentatives
Quelle est la convention idiomatique pour les constructeurs dans Go ?
// Quale firma è più idiomatica?Récapitulatif
- Rien
constructor: une fonctionNewTest utilisée (ouNewà l'intérieur du package du même nom). - Renvoie
*Tpour le cas simple ;(*T, error)avec préconditions ou validation. - En erreur : renvoie
nilcomme pointeur, l'erreur explicative comme deuxième valeur. - Pas de surcharges ; utilisez des noms descriptifs (
NewUserFromJSON,NewAdmin). - "Rendre la valeur zéro utile" : si vous réussissez, le constructeur ne sert à rien.
- Modèle avancé pour de nombreux paramètres optionnels : options fonctionnelles.