Lección 3 · 3 de 20
Incrustación y composición · Go
Componga tipos en Go incrustando structs: campos y métodos promovidos, cómo sustituir un método y por qué no es herencia. Clientes y direcciones.

Go no tiene herencia; tiene composición. Customer incluye Contact y Address escribiendo solo el tipo, sin nombre de campo: eso es un campo incrustado (*embedding*). El campo existe y se llama como su tipo, c.Contact y c.Address.
Los campos y métodos del tipo incrustado se promueven: c.Name es en realidad c.Contact.Name y c.City es c.Address.City. Lo mismo con los métodos: c.Label() y c.Line() funcionan como si Customer los hubiera declarado, y por tanto Customer cumple cualquier interfaz que pida Label() string.
La promoción atraviesa varios niveles. VIP incrusta Customer, así que v.Points += 50 modifica v.Customer.Points (de 120 a 170) y v.Contact.Email llega hasta el contacto, dos niveles más abajo.
VIP declara su propio Label, que tapa el promovido: v.Label() añade la categoría, [gold]. El original sigue ahí y se llama nombrándolo, v.Customer.Label(), que es lo que hace el nuevo método para no repetir código.
Ojo, no es herencia: un VIP no es un Customer y no se puede pasar donde se espera uno; hay que pasar v.Customer. Y si dos tipos incrustados tuvieran un campo con el mismo nombre, habría que indicar cuál con c.Contact.Name o c.Address.Name.
Código · main.go
package mainimport "fmt"type Contact struct{ Name, Email string }func (c Contact) Label() string { return c.Name + " <" + c.Email + ">" }type Address struct{ City, Country string }func (a Address) Line() string { return a.City + ", " + a.Country }// Customer embeds Contact and Address: their fields and methods are promoted.type Customer struct { Contact Address Points int}type VIP struct { // embeds Customer and overrides its Label method Customer Tier string}func (v VIP) Label() string { return "[" + v.Tier + "] " + v.Customer.Label() }func main() { c := Customer{Contact{"Ana Ruiz", "ana@shop.es"}, Address{"Seville", "Spain"}, 120} fmt.Println(c.Name, "|", c.Label(), "|", c.Line(), "|", c.Points, "points") v := VIP{Customer: c, Tier: "gold"} v.Points += 50 // promoted through two levels: v.Customer.Points fmt.Println(v.Label(), "|", v.Points, "points") fmt.Println(v.Customer.Label(), "|", v.Contact.Email, "|", v.Line())}Salida · go run main.go
Ana Ruiz | Ana Ruiz <ana@shop.es> | Seville, Spain | 120 points [gold] Ana Ruiz <ana@shop.es> | 170 points Ana Ruiz <ana@shop.es> | ana@shop.es | Seville, Spain
Para recordar
- Incrustar es escribir el tipo sin nombre de campo.
- Los campos y métodos del tipo incrustado se promueven.
- Un método propio con el mismo nombre tapa el promovido.
- El original sigue accesible:
v.Customer.Label(). - Es composición, no herencia: un
VIPno es unCustomer.
Ejercicio
Añada a Address un campo Postcode y haga que Line() lo incluya. Después defina una interfaz Labeler con Label() string y una función que reciba un Labeler: pásele c y v y compare lo que escribe.