Go desde cero, parte 5: structs, métodos y composición (clases no incluidas)
> serie: aprende go de 0 a 100 | artículo 5 de 19
Go no tiene clases, tampoco herencia, y aun así se escriben sistemas enormes con él. El truco, los structs en Go para los datos, métodos para el comportamiento y composición para reutilizar. Vamos por partes.
type Usuario struct {
Nombre string
Email string
Activo bool
}
// Literales: posicionales o con nombre (mejor con nombre)
u := Usuario{Nombre: "Jenn", Email: "jenn@ejemplo.dev", Activo: true}
// Acceso directo con punto. Sin getters ni setters.
u.Activo = false
Detalle con miga: la visibilidad en Go va por mayúsculas. Campo o función que empieza en mayúscula es exportado (público fuera del paquete), en minúscula es privado del paquete. No hay public ni private: la propia letra lo dice (A Tour of Go, exported names).
Métodos: funciones con receptor
Un método es una función con un parámetro especial delante, el receptor (A Tour of Go, methods):
type Rectangulo struct {
Ancho, Alto float64
}
// (r Rectangulo) es el receptor: convierte
// la función en método del tipo.
func (r Rectangulo) Area() float64 {
return r.Ancho * r.Alto
}
r := Rectangulo{Ancho: 3, Alto: 4}
fmt.Println(r.Area()) // 12
Si vienes de Python, el receptor es tu self pero con tipo y nombre corto. Si vienes de Java, es el this sacado a la firma. La gracia: puedes definir métodos sobre CUALQUIER tipo tuyo, no solo structs. Aquel type Nivel int del artículo 2 puede tener métodos.
// Receptor por valor: trabaja con una COPIA.
func (r Rectangulo) Duplica() {
r.Ancho *= 2 // se pierde al salir
}
// Receptor por puntero: modifica el original.
func (r *Rectangulo) DuplicaBien() {
r.Ancho *= 2
}
r.DuplicaBien() // Go hace (&r) por ti
Regla práctica para los structs en Go: si el método modifica el receptor o el struct es grande, puntero. Si es de solo lectura y pequeño, valor. Y por coherencia: si un método del tipo usa puntero, que lo usen todos. Los punteros tienen su artículo completo (el 10).
Composición: el embedding
Go no tiene herencia y lo dice sin complejos (FAQ, inheritance). Lo que ofrece es embedding: meter un tipo dentro de otro sin nombre de campo, y sus métodos y campos se promocionan al contenedor (Effective Go, embedding):
type Motor struct {
Caballos int
}
func (m Motor) Arranca() string {
return "brummm"
}
type Coche struct {
Motor // embebido: sin nombre de campo
Marca string
}
c := Coche{Motor: Motor{Caballos: 90}, Marca: "Retro"}
fmt.Println(c.Arranca()) // método promocionado
fmt.Println(c.Caballos) // campo promocionado
Esto no es herencia disfrazada: Coche no ES un Motor, TIENE un motor y delega en él. No hay jerarquías frágiles ni diamantes de la muerte, y el acoplamiento queda a la vista en el struct.
La pregunta de si Go es orientado a objetos tiene respuesta oficial: sí y no.(FAQ)
[Composición: el Motor presta sus métodos sin necesidad de herencia.]
El constructor que no es constructor
Sin clases no hay constructores. La convención: una función NewTipo que valida y devuelve puntero (Effective Go, composite literals):
Este sitio usa cookies. Unas son necesarias para que todo funcione; otras nos ayudan a entender qué páginas te interesan. Tú decides cuáles aceptas — y si rechazas alguna, es posible que algo deje de funcionar como esperas.
Funcional
Siempre activo
El almacenamiento técnico o el acceso son estrictamente necesarios para el fin legítimo de posibilitar la utilización de un servicio específico solicitado explícitamente por el abonado o usuario, o con el único fin de llevar a cabo la transmisión de una comunicación a través de una red de comunicaciones electrónicas.
Preferencias
El almacenamiento o acceso técnico es necesario para la finalidad legítima de almacenar preferencias que no han sido solicitadas por el abonado o el usuario.
Estadísticas
El almacenamiento o acceso técnico que se utiliza exclusivamente con fines estadísticosEl almacenamiento técnico o el acceso que se utiliza exclusivamente con fines estadísticos anónimos. Sin una citación judicial, el cumplimiento voluntario por parte de tu proveedor de servicios de Internet o registros adicionales de un tercero, la información almacenada o recuperada únicamente con este fin no se puede utilizar generalmente para identificarte.
Marketing
El almacenamiento o acceso técnico es necesario para crear perfiles de usuario con el fin de enviar publicidad, o para realizar un seguimiento del usuario en un sitio web o en varios sitios web con fines de marketing similares.