Lecciones del módulo (5/5)
El patrón de constructor `New...`
Go no tiene constructor como palabra clave. La convención es una
función de fábrica que devuelve un valor o puntero correctamente
inicializado. Por su nombre, normalmente se llama NewT (o Newxxx).
Patrón básico: NuevoT
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)Ventajas sobre el literal directo &User{...}:
- Centralizar validación y valores predeterminados.
- Puedes tener campos no exportados y completarlos sólo desde el constructor.
- Cambiar la implementación sin interrumpir a las personas que llaman.
Constructor con validación: (*T, error)
Cuando la inicialización pueda fallar, devuelva (*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...
}Este es el patrón idiomático para cualquier constructor con Condiciones previas: conexión a la base de datos, identificador de archivos, configuración del servidor, etc.
Múltiples constructores
Si tiene varias "formas" de construir, utilice nombres descriptivos:
func NewUser(name string) *User { ... }
func NewUserFromJSON(data []byte) (*User, error) { ... }
func NewAdmin() *User { ... }Sin sobrecargas en Go: el nombre destaca.
Opciones funcionales (nota avanzada)
Cuando los parámetros son muchos y opcionales, el patrón idiomático es "opciones funcionales":
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"))Es más detallado que literal pero se adapta bien a muchos parámetros.
opciones y mantiene la compatibilidad con versiones anteriores (agregue un nuevo Option
no interrumpe a las personas que llaman existentes).
Cuando NO necesitas un constructor
Si el valor cero del tipo ya es útil, deje que la persona que llama use el
literal directo. Ejemplo canónico: sync.Mutex no tiene constructor,
var m sync.Mutex ya es un mutex listo. Lo mismo para bytes.Buffer.
Se aplica la regla: "Hacer útil el valor cero". Si lo logras nada constructor obligatorio.
Pruébalo tú mismo
Escribe la función de fábrica NewUser(name string, age int) *Usuario que devuelve &User{Nombre: nombre, Edad: edad}.
Mostrar pista
Utilice el literal `&User{...}` para devolver un puntero.
Solución disponible después de 3 intentos
Transforme NewUser en (cadena de nombre) (*Usuario, error): si nombre == '' devuelve nulo, errores.Nuevo('nombre requerido').
Mostrar pista
Si hay condiciones previas, devuelve (*T, error) y devuelve nil + error en caso de error.
Solución disponible después de 3 intentos
¿Cuál es la convención idiomática para los constructores en Go?
// Quale firma è più idiomatica?Resumen
- Nada
constructor: se utiliza una funciónNewT(oNewdentro del paquete del mismo nombre). - Devuelve
*Tpara el caso simple;(*T, error)con condiciones previas o validación. - En error: devuelve
nilcomo puntero, el error explicativo como segundo valor. - Sin sobrecargas; utilice nombres descriptivos (
NewUserFromJSON,NewAdmin). - "Hacer útil el valor cero": si lo consigues, el constructor es inútil.
- Patrón avanzado para muchos parámetros opcionales: opciones funcionales.