Education Mobility Solutions SAS
ProfesorDavid Gonzalez
¡Increíble aporte!
JUAN PABLO MAYORGA MENDIETA
Excelente aporte, muchas gracias!!
Dominar las interfaces en Go es un paso fundamental para escribir código más limpio, reutilizable y eficiente. Cuando múltiples structs comparten un mismo método, las interfaces permiten crear un punto de entrada único que simplifica la interacción con todas esas estructuras de datos. Además, existe una técnica avanzada que permite simular listas con múltiples tipos de datos mediante listas de interfaces, algo que en Go no es posible de forma nativa con slices convencionales.
¿Cómo se definen structs y métodos para figuras geométricas en Go?
El punto de partida consiste en declarar los structs que representan las figuras. En este caso, se trabaja con un cuadrado y un rectángulo [01:12].
cuadrado tiene un solo campo: base de tipo float64.rectángulo tiene dos campos: base y altura, ambos de tipo float64.Cada struct necesita su propia función de área. Para el cuadrado, el área se calcula multiplicando la base por sí misma (c.base * c.base). Para el rectángulo, se multiplica base por altura (r.base * r.altura) [02:08].
Una vez creados los structs, se instancian con valores concretos. Por ejemplo, un cuadrado con base 2 y un rectángulo con base 2 y altura 4.
¿Por qué no basta con llamar cada método por separado?
Sin interfaces, para conocer el área de cada figura habría que invocar directamente miCuadrado.area() y miRectangulo.area() [04:18]. Esto funciona con dos figuras, pero cuando tienes muchas más estructuras de datos que comparten la misma función, el código se vuelve repetitivo y difícil de mantener.
¿Qué es una interfaz y cómo se implementa en Go?
Una interfaz en Go es un tipo que agrupa métodos compartidos por distintos structs. Se declara con type seguido del nombre y la palabra clave interface [02:50].
go type figuras2D interface { area() float64 }
Esta interfaz establece que cualquier struct que tenga un método area() retornando float64 cumple automáticamente con la interfaz figuras2D. No hace falta declarar explícitamente que un struct implementa la interfaz; Go lo resuelve de forma implícita.
Después se crea una función llamada calcular que recibe como parámetro la interfaz [03:12]:
go func calcular(f figuras2D) { fmt.Println("Area:", f.area()) }
¿Cómo se ejecuta la interfaz con diferentes structs?
Simplemente se pasa cada instancia a la función calcular [04:30]:
go calcular(miCuadrado) calcular(miRectangulo)
Al ejecutar, el cuadrado con base 2 muestra un área de 4, mientras que el rectángulo con base 2 y altura 4 muestra un área de 8. Aunque ambos llaman al mismo método area(), la interfaz selecciona automáticamente la implementación correspondiente al struct que recibe. Esto hace el código mucho más eficiente y legible a gran escala [05:05].
¿Cómo crear una lista de interfaces con múltiples tipos de datos?
En lenguajes más flexibles, una misma lista puede contener valores de distintos tipos: textos, números, booleanos. En Go, al crear un slice o array, es obligatorio indicar el tipo de dato. Sin embargo, existe una forma de simular ese comportamiento usando una lista de interfaces vacías [05:38].
go myInterface := []interface{}{"hola", 12, 4.5} fmt.Println(myInterface...)
interface{}....) al imprimir indican que cada elemento se muestra de forma individual.Al ejecutar, la consola muestra los resultados de las áreas calculadas previamente y la lista de interfaces con sus distintos tipos de datos: un string, un entero y un decimal [06:22].
Esta técnica es especialmente útil cuando necesitas manejar colecciones heterogéneas sin perder la seguridad de tipos que caracteriza a Go.
Si quieres practicar, un buen reto es crear interfaces para distintas figuras geométricas, organizar los structs en paquetes separados y definir métodos privados y públicos según corresponda. Comparte tu solución en los comentarios.
Education Mobility Solutions SAS
ProfesorDavid Gonzalez
¡Increíble aporte!
JUAN PABLO MAYORGA MENDIETA
Excelente aporte, muchas gracias!!
IVAN DARIO CHIPATECUA CABEZAS
Said Hernandez
Buen Codigo
Williams Bricaire Vallejo Mariscal
Solo como comentario, la altura en Ingles es Height no High. Saludos!
Si se les dificulto un poco el funcionamiento de las interfaces en Golang (ya que en Java o Python son más como contratos, en donde cumplimos el implementar una función en una clase), espero que mi explicación les ayude
Tomo a las interfaces más como herramientas para hacer parámetros de Structs en funciones universales, me explico:
Creamos esta función para calcular el area de cuadrado
func (c cuadrado) area() float64{ return c.base * c.base }
Normalmente tendríamos que invocar el método del struct cada que queramos usar la función
myCuadrado := cuadrado{base:2} myCuadrado.area()
Pero gracias a las interfaces, ahora podemos pasar el Struct myCuadrado como parametro y reutilizar una misma funcion en multiples Structs
//Creamos un Struct triangulo type triangulo struct{ base float64 altura float64 } //Instanciamos el struct triangulo myTriangulo := triangulo{base: 2, altura: 4} //Creamos la interface type figuraGeometrica2d interface{ area() float64 } //Ahora tenemos una función que toma como parametro Structs func calcularArea(f figuraGeometrica2d){ fmt.Println("El area de tu figura es de: ", f.area) }
Y ahora, gracias a la interface, podemos pasar a todo Struct que tenga un método area() como parametro de la funcion calcularArea
calcularArea(myCuadrado) calcularArea(myTriangulo)
Si se les dificultó entender el concepto o uso de interface, espero haberles ayudado (:
Wilson Fernando Antury Torres
Steven Javier Arevalo Poveda
Quizá es bueno tener en cuenta que aquí no se implementan interfaces explicitamente sino implicitamente, es por eso que solo con que el método se llame área se hace la asociación!
Julio Cesar Estrada Marcial
Algo que no se menciona en el curso y puede generar duda es como la función calculate sabe que los structs que pasamos están implementando la interfaz que estamos esperando. Esto pasa porque GO de forma implícita implementa la interfaz cuando un struct tiene todos los métodos que esa interfaz necesita.
Adicional recuerden que en ese ejemplo se aplican algunos principios SOLID como: Interface segregation principle, Liskov substitution y Dependency inversion principle.
Gabriel Thomas
Hola a todos/todas. tengo una duda. Como go sabe que la estructura implementa la interfaz. En java, normalmente lo que uno hace es decirle a cada clase que interfaz implementa con el keyword implements . En este ejemplo, no veo que a la estructura se le diga explícitamente que va a implementar la interfaz. De antemano muchas gracias
Osmandi Gomez
Eso es porque Go lo hace de forma implícita, ya que utiliza Duck typing (Si camina como pato y hace quack como un pato, entonces es un pato)
Adrián Olmedo
En Go no existe la palabra clave implements ni parecido, una estructura implementa una interfaz de forma implícita, cuando tiene métodos con el mismo nombre y firma que los que tiene la interfaz.
Si quieres saber visualmente cuáles estructuras en tu código implementan tal cual interfaz puedes usar la herramienta goplantuml https://github.com/jfeliu007/goplantuml
Fernando Eladio Alvarez Noya
Nicolas Estefan Lopez
Holas Osmandi, seria chevere que pudieras agregar a este video la importancia de las interfaces, teniendo en cuenta que hay personas que vienen sin saber POO.
Saludos.
Eliaz Bobadilla
Me gusta mucho la forma de declarar las listas de interfaces:
package main import "fmt" func main() { myInterface := []interface{}{"Hola", 121, 4.90} fmt.Println(myInterface...) }
Mas documentado aquí.
Jhon Eduard Bocanegra Ortiz
Miguel Angel Luque Acevedo
Chris Amachi
Código para usar un poder especial de diferentes formas con diferentes pokemon. El resultado: la cantidad de poder que aumenta el pokemon.
package main import "fmt" type pokemonPwrs interface { usePwr() int } type myElecPokemon struct { name string volts int level int } type myFirePokemon struct { name string flames int level int } func (fPoke myFirePokemon) usePwr() int { return fPoke.flames * fPoke.level } func (ePoke myElecPokemon) usePwr() int { return ePoke.volts * ePoke.level } func useSpecialPwrs(p pokemonPwrs) { fmt.Printf("Power: %v \n", p.usePwr()) } func main() { var pikachu = myElecPokemon{name: "Pikachu", volts: 10, level: 3} var litten = myFirePokemon{name: "Litten", flames: 25, level:5} useSpecialPwrs(pikachu) useSpecialPwrs(litten) }
Oscar Callisaya
Me tomo un tiempo entender q es la lista de interfaces, este stackoverflow explica bien por q la repeticion de {}{} https://stackoverflow.com/questions/57149795/what-does-the-double-curly-brace-mean-in-interface/57150132
Este feature de Go, te permite crear un slice indicando q INTERFACE pertenecen todos los items creados:
// Slice de objetos de la interfaz figuras2D figuras := []figuras2D{cuadrado{base: 2}, rectangulo{base: 2, altura: 4}} for _, figura := range figuras { fmt.Println(figura) calcular(figura) }
Y al intentar colocar un objeto q no tiene la implementacion de esa interfaz, pues el editor y al correr te indicaran q no implementa lo q indica la interfaz. Ese feature realmente me gusto
// Agregando un STRING en el slice figuras := []figuras2D{cuadrado{base: 2}, rectangulo{base: 2, altura: 4}, "invalid object"} // Al ejecutarlo PS C:\git\test-go> go run .\src\interfacesListas.go # command-line-arguments src\interfacesListas.go:55:76: cannot use "invalid object" (constant of type string) as type figuras2D in array or slice literal: string does not implement figuras2D (missing area method)
Rodolfo Valencia Rodriguez
yo diria que esta clase explica un poco de polimorfismo con las estructuras e interfaces
Josue Martin Chi Perera
Fernando Eladio Alvarez Noya
Recordando Java y ocultando los atributos del struct me puse a jugar con lo siguiente:
package main import ( cua "curso_golang_basico/src/12Interfaces/pkgcuadrado" "fmt" ) func main() { var micuadrado cua.Cuadrado micuadrado.CreaLado(4) fmt.Println("Imprimo micuadrado: ", micuadrado) }
Y en el package...
package pkgcuadrado import "fmt" type Cuadrado struct { lado int } func (c *Cuadrado) CreaLado(l int) { c.lado = l } func (c Cuadrado) String() string { return fmt.Sprintf("Cuadrado de lado = %d", c.lado) }
De esta manera no hago publico informacion que no estaria bueno exponer :)
Luna Mariana Cagua Clavijo
tengo mis dudas sobre las importaciones, no me es algo tan facil, pero asi quedo :)
AGEO ANDRÉS FUENTES MERIÑO
Reto cumplido
Vale aclara algo:
las funciones en Go se definen de la siguiente manera:
func nameFunc() { // body de la función }
El nombre de las funciones no se pueden repetir en un mismos paquete, pero que esta pasando con las func area que se definieron dos veces en esta clase?
Lo que esta sucediendo es que no son simplemente funciones, en Go se les conoce como Methods. Un método es una función con un argumento de receptor especial. que debe de ser de tipo struct o interface y se definen de la siguiente manera:
func (receiver type) nameFunc() { // body del metodo }
Una buena practica es nombrar a nuestro receptor con la primera letra del struct o interface a la que estamos haciendo referencia, por ejemplo:
type cuadrado struct { lado float32 } func (c cuadrado) area() float32 { return c.lado * c.lado }
Reto
package main import ( "fmt" "math" "reflect" ) type figure2D interface { getArea() float64 } type square struct { base float64 } type rectangle struct { high float64 width float64 } type trapezoid struct { baseA float64 baseB float64 high float64 } type circle struct { radio float64 } func (s square) getArea() float64 { return s.base * s.base } func (r rectangle) getArea() float64 { return r.high * r.width } func (t trapezoid) getArea() float64 { return ((t.baseA + t.baseB) / 2) * t.high } func (c circle) getArea() float64 { return math.Pi * math.Pow(c.radio, 2) } func calculateArea(f figure2D) { fmt.Printf("Area of %s: %.2f\n", reflect.TypeOf(f).Name(), f.getArea()) } func main() { mySquare := square{base: 5} myRectangle := rectangle{width: 4, high: 5} myTrapezoid := trapezoid{baseA: 18, baseB: 10, high: 5} myCircle := circle{radio: 4} calculateArea(mySquare) calculateArea(myRectangle) calculateArea(myTrapezoid) calculateArea(myCircle) }
Salio con fritas...
Este es el principal
package main import ( cua "curso_golang_basico/src/12Interfaces/pkgcuadrado" inter "curso_golang_basico/src/12Interfaces/pkginterfaces" rec "curso_golang_basico/src/12Interfaces/pkgrectangulo" "fmt" ) func main() { var micuadrado cua.Cuadrado micuadrado.CreaLado(4) var mirec rec.Rectangulo mirec.CreaRec(8, 3) fmt.Println(micuadrado) fmt.Println(mirec) // Sin interfaces fmt.Println("\nSin interfaces: ") fmt.Println("Area cuadrado: ", micuadrado.Area()) fmt.Println("Area rectangulo: ", mirec.Area()) // Con interfaces fmt.Println("\nCon interfaces") inter.Calcular(micuadrado) inter.Calcular(mirec) }
Este es el package cuadrado (un subdir dentro del principal)
package pkgcuadrado import "fmt" type Cuadrado struct { lado int } func (c *Cuadrado) CreaLado(l int) { c.lado = l } func (c Cuadrado) String() string { return fmt.Sprintf("Cuadrado de lado = %d", c.lado) } func (c Cuadrado) Area() int { return c.lado * c.lado }
Este es ahora el package rectangulo
package pkgrectangulo import "fmt" type Rectangulo struct { largo int ancho int } func (r *Rectangulo) CreaRec(l, a int) { r.largo = l r.ancho = a } func (r Rectangulo) String() string { return fmt.Sprintf("Rectangulo de largo = %d y ancho = %d", r.largo, r.ancho) } func (r Rectangulo) Area() int { return r.ancho * r.largo }
Y finalmente poniendome creativo un package para las interfaces...
package pkginterfaces import "fmt" type figuras2D interface { Area() int } func Calcular(f figuras2D) { fmt.Println("Area: ", f.Area()) }
Y lo mejor de todo es que anda! :)
Reto 🥳
//Main package main import ( pk "curso_golang_platzi/src/mypackage" f "fmt" ) func main() { myCuadrado := pk.Cuadrado{Base: 4} f.Println(myCuadrado) pk.Calcular(myCuadrado) myRectangulo := pk.Rectangulo{Base: 2, Altura: 4} f.Println(myRectangulo) pk.Calcular(myRectangulo) myCirculo := pk.Circulo{Radio: 5} f.Println(myCirculo) pk.Calcular(myCirculo) } //mypackage package mypackage import "fmt" type Figuras2D interface { area() float64 } type Cuadrado struct { Base float64 } type Rectangulo struct { Base float64 Altura float64 } type Circulo struct { Radio float64 } func (c Cuadrado) area() float64 { return c.Base * c.Base } func (r Rectangulo) area() float64 { return r.Base * r.Altura } func (ci Circulo) area() float64 { return (ci.Radio * ci.Radio) * 3.1415 } func Calcular(f Figuras2D) { fmt.Println("Área:", f.area()) }
Reto: main.go
package main import ( fg "curso-golang/src/18.Interfaces/figuras" "fmt" ) type figuras2D interface { Area() float64 } func calcular(f figuras2D) { fmt.Printf("Area: %v\n", f.Area()) } func main() { miCuadrado := fg.NewCuadrado(2) miRectangulo := fg.NewRectangulo(2, 4) miCirculo := fg.NewCirculo(3.2) miTriangulo := fg.NewTriangulo(7.8, 2.9) calcular(miCuadrado) calcular(miRectangulo) calcular(miCirculo) calcular(miTriangulo) }
cuadrado.go
package figuras type cuadrado struct { base float64 } func NewCuadrado(bs float64) cuadrado { return cuadrado{base: bs} } func (c cuadrado) Area() float64 { return c.base * c.base }
rectangulo.go
package figuras type rectangulo struct { base float64 altura float64 } func NewRectangulo(bs, alt float64) rectangulo { return rectangulo{base: bs, altura: alt} } func (r rectangulo) Area() float64 { return r.altura * r.base }
circulo.go
package figuras import "math" type circulo struct { radio float64 } func NewCirculo(rd float64) circulo { return circulo{radio: rd} } func (c circulo) Area() float64 { return math.Pi * c.radio * c.radio }
triangulo.go
package figuras type triangulo struct { base float64 altura float64 } func NewTriangulo(bs, alt float64) triangulo { return triangulo{base: bs, altura: alt} } func (t triangulo) Area() float64 { return t.base * t.altura / 2 }
Reto completo
Archivo main
package main import ( mpk "curso_golang_basico/src/mypackage" "fmt" ) func main() { //Entrada de valores myCuadrado := mpk.Cuadrado{Base: 2} myRectangulo := mpk.Rectangulo{Base: 3, Altura: 5} //Impresión de valores fmt.Println(myCuadrado) fmt.Println(myRectangulo) //Calculo de valores mpk.Calcular(myCuadrado) mpk.Calcular(myRectangulo) //SALIDA // Un lado del cuadrado mide: 2 cm // La base del rectangulo mide: 3 cm y la altura del rectangulo mide: 5 cm // El área es: 4 cm2 // El área es: 15 cm2 }
Archivo mypackage
package mypackage import "fmt" //PARTE 4. type figura2D interface { Area() float64 } //PARTE 1. type Cuadrado struct { Base float64 } //PARTE 1. type Rectangulo struct { Base float64 Altura float64 } //Parte 3. func (c Cuadrado) Area() float64 { return c.Base * c.Base } //PARTE 3. func (r Rectangulo) Area() float64 { return r.Base * r.Altura } //PARTE 5. func Calcular(f figura2D) { fmt.Println("El área es:", f.Area(), "cm2") } //Para la impresión del valor entrante de uncuadrado func (C Cuadrado) String() string { return fmt.Sprintf("Un lado del cuadrado mide: %v cm", C.Base) } //Para la impresión de los valores entrantes de un rectangulo func (C Rectangulo) String() string { return fmt.Sprintf("La base del rectangulo mide: %v cm y la altura del rectangulo mide: %v cm", C.Base, C.Altura) }