33 - Declaración e implementación de interfaces en Go

Una interface declara una serie de comportamientos que deben ser implementados por tipos de datos struct.

Una interface solo especifica uno o más cabeceras de métodos. La interface indica que se debe implementar pero no indica nada como se lo debe implementar.

Una interface puede ser implementada por cualquier struct, con esto logramos agrupar clases con características similares.

Por ejemplo podemos tener dos struct que representen un avión y un helicóptero. Luego plantear una interface con un método llamado volar. Los dos struct pueden implementar dicha interface y codificar el método volar (los algoritmos seguramente sean distintos pero el comportamiento de volar es común tanto a un avión como un helicóptero)

Programa: ejercicio142.go

package main

import "fmt"

type Punto interface {
    Imprimir()
}

type PuntoPlano struct {
    x, y int
}

type PuntoEspacio struct {
    x, y, z int
}

func (p PuntoPlano)Imprimir() {
    fmt.Println(p.x, p.y)
}

func (p PuntoEspacio)Imprimir() {
    fmt.Println(p.x, p.y, p.z)
}

func Recorrer(vec [2]Punto) {
    for f := 0; f < len(vec); f++ {
        vec[f].Imprimir()
    }
}


func main() {
    var vec [2]Punto
    vec[0] = PuntoPlano{x:3,y:3}
    vec[1] = PuntoEspacio{x:1,y:2,z:3}
    Recorrer(vec)
}

Para declarar una interface en Go utilizamos la palabra clave type y seguidamente el nombre del tipo y la palabra clave interface. Luego entre llaves indicamos todas las cabeceras de métodos que se deben implementar. En nuestro ejemplo declaramos la interface Punto e indicamos que quien la implemente debe definir un método llamdo Imprimir sin parámetros y que no retorna nada:

type Punto interface {
    Imprimir()
}

Por otro lado declaramos dos tipos de datos struct llamados PuntoPlano con dos campos y PuntoEspacio con tres campos:

type PuntoPlano struct {
    x, y int
}

type PuntoEspacio struct {
    x, y, z int
}

Ahora implementamos el método Imprimir de la interface Punto para el struct PuntoPlano (el objetivo de este método es mostrar los atributos x e y):

func (p PuntoPlano)Imprimir() {
    fmt.Println(p.x, p.y)
}

De forma similar implementamos el método Imprimir de la interface Punto para el struct PuntoEspacio (el objetivo de este método es mostrar los atributos x, y "y" z):

func (p PuntoEspacio)Imprimir() {
    fmt.Println(p.x, p.y, p.z)
}

En la main definimos un vector de 2 elementos de la interface Punto:

func main() {
    var vec [2]Punto

En la primer componente almacenamos un struct de tipo PuntoPlano:

    vec[0] = PuntoPlano{x:3,y:3}

Y en la segunda componente almacenamos un struct de tipo PuntoEspacio:

    vec[1] = PuntoEspacio{x:1,y:2,z:3}

Llamamos a la función Recorrer y e enviamos el vector:

    Recorrer(vec)

La función Recorrer recibe el vector y llama al método Imprimir:

func Recorrer(vec [2]Punto) {
    for f := 0; f < len(vec); f++ {
        vec[f].Imprimir()
    }
}

Según el tipo de struct que se guarda en el vector se llamará el Imprimir que le corresponde.

Problema 1:

Se tiene la siguiente interface:

type Figura interface {
    Perimetro() int
    Superficie() int
}

Definir dos struct que representen un Cuadrado y un Rectángulo. Implementar la interface Figura en ambos struct.

Programa: ejercicio1.go

package main

import "fmt"

type Figura interface {
    Perimetro()
    Superficie()
}

type Cuadrado struct {
    Lado int
}

type Rectangulo struct {
    LadoMenor int
    LadoMayor int
}

func (cuad Cuadrado) Perimetro() {
    fmt.Println("El perímetro del cuadrado de lado:", cuad.Lado, " es ", cuad.Lado * 4)
}

func (cuad Cuadrado) Superficie() {
    fmt.Println("La superficie del cuadrado de lado:", cuad.Lado, " es ", cuad.Lado * cuad.Lado)    
}

func (rect Rectangulo) Perimetro() {
    fmt.Println("El perímetro del rectángulo de lado menor:", rect.LadoMenor," y lado mayor:", rect.LadoMayor, " es ", (rect.LadoMayor * 2) + (rect.LadoMenor * 2))
}

func (rect Rectangulo) Superficie() {
    fmt.Println("La superficie del rectángulo de lado menor:", rect.LadoMenor, " y lado mayor:", rect.LadoMayor, " es ", rect.LadoMenor * rect.LadoMayor)
}

func Calcular(fig [3]Figura) {
    for f := 0; f < len(fig); f++ {
        fig[f].Superficie()
        fig[f].Perimetro()
    }
}

func main() {
    var vec [3] Figura
    vec[0] = Cuadrado{Lado: 10}
    vec[1] = Cuadrado{Lado: 6}
    vec[2] = Rectangulo{LadoMenor: 10, LadoMayor: 20}
    Calcular(vec)
}

Definimos dos struct para almacenar el lado de un cuadrado y el lado menor y lado mayor de un rectángulo:

type Cuadrado struct {
    Lado int
}

type Rectangulo struct {
    LadoMenor int
    LadoMayor int
}

Implementamos la interface Figura para ambos struct:

func (cuad Cuadrado) Perimetro() {
    fmt.Println("El perímetro del cuadrado de lado:", cuad.Lado, " es ", cuad.Lado * 4)
}

func (cuad Cuadrado) Superficie() {
    fmt.Println("La superficie del cuadrado de lado:", cuad.Lado, " es ", cuad.Lado * cuad.Lado)    
}

func (rect Rectangulo) Perimetro() {
    fmt.Println("El perímetro del rectángulo de lado menor:", rect.LadoMenor," y lado mayor:", rect.LadoMayor, " es ", (rect.LadoMayor * 2) + (rect.LadoMenor * 2))
}

func (rect Rectangulo) Superficie() {
    fmt.Println("La superficie del rectángulo de lado menor:", rect.LadoMenor, " y lado mayor:", rect.LadoMayor, " es ", rect.LadoMenor * rect.LadoMayor)
}

En la main definimos un vector de tres elementos de tipo Figura y almacenamos dos cuadrados y un rectángulo:

func main() {
    var vec [3] Figura
    vec[0] = Cuadrado{Lado: 10}
    vec[1] = Cuadrado{Lado: 6}
    vec[2] = Rectangulo{LadoMenor: 10, LadoMayor: 20}

Para mostrar la superficie y perímetro de cada figura implementamos una función que le enviamos el vector y llamamos a los dos métodos propuestos por la interface Figura:

func Calcular(fig [3]Figura) {
    for f := 0; f < len(fig); f++ {
        fig[f].Superficie()
        fig[f].Perimetro()
    }
}