Lecții din modul (5/5)
Modelul constructor `Nou...`
Go nu are un cuvânt cheie constructor. Convenția este o fabrică
funcție care returnează o valoare sau un pointer inițializat corect. De către
nume este de obicei numit NewT (sau NewXxx).
Model de bază: 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)Beneficii față de literalul direct &User{...}:
- centralizați validarea și setările implicite.
- Puteți avea câmpuri neexportate și le puteți popula doar din constructor.
- Puteți schimba implementarea fără a întrerupe apelanții.
Constructor cu validare: (*T, eroare)
Când inițializarea poate eșua, returnați (*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 {
// handle...
}Acesta este modelul idiomatic pentru orice constructor cu precondiții: conexiune db, handle de fișier, configurare server etc.
Constructori multipli
Dacă aveți mai multe „căi” de a construi, utilizați nume descriptive:
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }Fără supraîncărcare în Go: numele dezambiguizează.
Opțiuni funcționale (viziune avansată)
Când există mulți parametri opționali, modelul idiomatic este „opțiuni funcționale”:
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"))Este mai verbos decât un literal, dar se scalează bine cu multe opționale
parametrii și păstrează compatibilitatea cu înapoi (adăugarea unui nou Option
nu distruge apelanții existenți).
Când NU aveți nevoie de un constructor
Dacă valoarea zero a tipului este deja utilă, lăsați apelantul să folosească
direct literal. Exemplu canonic: sync.Mutex nu are constructor,
var m sync.Mutex este deja un mutex gata de utilizare. Același lucru pentru bytes.Buffer.
Regula este: „Fă utilă valoarea zero”. Dacă poți, nu constructor obligatoriu.
Încearcă
Scrieți funcția din fabrică NewUser(șir de nume, vârstă int) *Utilizator care returnează &Utilizator{Nume: nume, Vârstă: vârstă}.
Afișează indiciu
Utilizați literalul `&Utilizator{...}` pentru a returna un pointer.
Soluție disponibilă după 3 încercări
Transformați NewUser în (șir de nume) (*Utilizator, eroare): if name == '' return nil, errors.New('nome obligatoriorio').
Afișează indiciu
Dacă există precondiții, returnați (*T, eroare) și returnați zero + eroare la eșec.
Soluție disponibilă după 3 încercări
Care este convenția idiomatică pentru constructori în Go?
// Which signature is more idiomatic?Recapitulare
- Nu
constructor: utilizați o funcțieNewT(sauNewîn interiorul pachetului cu același nume). - Returnează
*Tpentru cazul simplu;(*T, error)cu precondiții sau validare. - În caz de eroare: returnați
nilca indicator și eroarea explicativă ca a doua valoare. - Fara supraincarcare; utilizați nume descriptive (
NewUserFromJSON,NewAdmin). - „Fă utilă valoarea zero”: dacă poți, constructorul este inutil.
- Model avansat pentru mulți parametri opționali: opțiuni funcționale.