Lekcje modułu (5/5)
Wzorzec konstruktora `New...`
Go nie ma słowa kluczowego constructor. Konwencja to fabryka
funkcja, która zwraca prawidłowo zainicjowaną wartość lub wskaźnik. Przez
nazwa, zwykle nazywa się NewT (lub NewXxx).
Wzór podstawowy: 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)Korzyści w porównaniu z bezpośrednim dosłownym &User{...}:
- centralizujesz walidację i ustawienia domyślne.
- Możesz mieć niewyeksportowane pola i wypełniać je tylko z poziomu konstruktora.
- Możesz zmienić implementację bez przerywania rozmówców.
Konstruktor z walidacją: (*T, błąd)
Jeśli inicjalizacja może się nie udać, zwróć (*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...
}Jest to idiomatyczny wzór dla każdego konstruktora warunki wstępne: połączenie z bazą danych, uchwyt pliku, konfiguracja serwera itp.
Wielu konstruktorów
Jeśli masz wiele „sposób” konstruowania, użyj nazw opisowych:
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }Żadnych przeciążeń w Go: nazwa jest jednoznaczna.
Opcje funkcjonalne (zaawansowany podgląd)
Gdy istnieje wiele opcjonalnych parametrów, wzorcem idiomatycznym jest „opcje funkcjonalne”:
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"))Jest bardziej szczegółowy niż dosłowny, ale dobrze się skaluje z wieloma opcjonalnymi
parametry i zachowuje kompatybilność wsteczną (dodanie nowego Option
nie przerywa istniejących rozmówców).
Kiedy NIE potrzebujesz konstruktora
Jeśli wartość zerowa typu jest już użyteczna, pozwól wywołującemu użyć metody
bezpośrednie, dosłowne. Przykład kanoniczny: sync.Mutex nie ma konstruktora,
var m sync.Mutex jest już gotowym do użycia muteksem. To samo z bytes.Buffer.
Zasada brzmi: „Uczyń wartość zerową użyteczną”. Jeśli możesz, nie obowiązkowy konstruktor.
Spróbuj
Napisz funkcję fabryczną NewUser(nazwa string, wiek int) *Użytkownik zwracający &User{Nazwa: imię, Wiek: wiek}.
Pokaż wskazówkę
Użyj literału `&User{...}`, aby zwrócić wskaźnik.
Rozwiązanie dostępne po 3 próbach
Zamień NewUser na (ciąg nazwy) (*User, błąd): if name == '' zwróć zero, błędy.New('nome obbligatorio').
Pokaż wskazówkę
Jeśli istnieją warunki wstępne, zwróć (*T, błąd) i zwróć zero + błąd w przypadku niepowodzenia.
Rozwiązanie dostępne po 3 próbach
Jaka jest idiomatyczna konwencja dotycząca konstruktorów w Go?
// Which signature is more idiomatic?Podsumowanie
- Brak
constructor: użyj funkcjiNewT(lubNeww pakiecie o tej samej nazwie). - Zwróć
*Tdla prostego przypadku;(*T, error)z warunkami wstępnymi lub walidacją. - W przypadku błędu: zwróć
niljako wskaźnik i błąd objaśniający jako drugą wartość. - Brak przeciążeń; używaj nazw opisowych (
NewUserFromJSON,NewAdmin). - „Uczyń wartość zerową użyteczną”: jeśli możesz, konstruktor jest niepotrzebny.
- Zaawansowany wzór dla wielu opcjonalnych parametrów: opcje funkcjonalne.