No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Interfaces

10/30
Recursos

¿Cómo funcionan las interfaces en la programación orientada a objetos?

Las interfaces son una herramienta esencial para la programación orientada a objetos, ya que permiten la implementación del polimorfismo y diversos patrones de diseño. Consideradas como "contratos", obligan a las clases que deciden utilizarlas a implementar cada una de las funciones o métodos que estas definen. La importancia de las interfaces es evidente en su capacidad de simplificar procesos de codificación y gestionar de manera eficiente el comportamiento de diferentes clases.

¿Qué es una interfaz en programación?

Una interfaz en programación se define como un conjunto de métodos que una clase debe implementar para cumplir con un contrato particular. En este sentido:

  • Definición de contrato: Una interfaz asegura que cualquier clase que la implemente debe contener los métodos especificados.
  • Flexibilidad en la implementación: Aunque una interfaz estipula cuáles métodos deben existir, no dicta cómo estos deben ser implementados.

En TypeScript, por ejemplo, se puede encontrar una interfaz denominada printInfo, que requiere una función getMessage que devuelve un string. Sin entrar en detalles de implementación, cualquier clase que quiera "firmar" este contrato deberá asegurarse de que el método getMessage exista dentro de su estructura.

¿Cómo se implementan las interfaces en TypeScript y Go?

Uso de interfaces en TypeScript

  • Declaración: Utiliza la palabra clave interface para definir qué métodos deben estar presentes.
  • Implementación: Las clases usan la palabra clave implements para indicar que están adoptando la interfaz.

Un ejemplo en TypeScript muestra cómo una clase puede implementar diferentes interfaces mientras mantiene su propia lógica interna única. Esta flexibilidad permite a distintos desarrolladores implementar la funcionalidad necesaria de forma peculiar a la estructura de su aplicación, apoyando el comportamiento polimórfico.

Implementación implícita en Go

A diferencia de TypeScript, Go utiliza un enfoque implícito para la implementación de interfaces:

  • Declaración: Similar a otros lenguajes, Go permite definir un tipo de interfaz, pero aquí, las clases que coinciden con la interfaz se consideran que la implementan automáticamente.
  • Polimorfismo: Mediante métodos comunes en diferentes estructuras (struct), Go permite operaciones polimórficas utilizando las interfaces.

El siguiente código ilustra la creación de una interfaz printInfo en Go:

type printInfo interface {
    getMessage() string
}

Dos structs, TemporaryEmployee y FullTimeEmployee, pueden implementar implícitamente esta interfaz si definen un método getMessage() correctamente.

¿Por qué son importantes las interfaces?

El uso de interfaces en programación no solo fomenta un diseño más limpio y modular, sino que también reduce la duplicación de código. Al utilizar interfaces, no es necesario definir múltiples funciones printMessage para cada clase, sino que se puede trabajar con un solo método genérico que acepte cualquier objeto que implemente la interfaz. Esto:

  • Reduce la repetición: Evita crear funciones duplicadas para cada clase.
  • Facilita la extensión: Al añadir nuevas clases, solo se necesita implementar la interfaz sin cambiar el código existente.
  • Fomenta el polimorfismo: Permite que diferentes clases sean tratadas a través de una misma interfaz, expandiendo las posibilidades de reutilización y adaptación del código.

Reflexiones finales

Las interfaces son, sin duda, una joya en la corona de la programación orientada a objetos y en lenguajes como Go. Aunque su implementación pueda variar entre lenguajes, el concepto básico sigue siendo una pieza inquebrantable en la creación de códigos robustos, adaptables y eficientes. Alentamos a los desarrolladores a profundizar su comprensión y uso de estas potentes herramientas para maximizar la eficacia de sus proyectos. Y recuerda, el verdadero poder de la programación yace en la capacidad de seguir aprendiendo y adaptando diversas técnicas a tus necesidades de desarrollo. ¡Sigue explorando y codificando!

Aportes 15

Preguntas 5

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Les comparto mis implementaciones de getMessage para FullTimeEmployee y TemporaryEmployee 😃:

func (ftEmployee FullTimeEmployee) getMessage() string {
	return fmt.Sprintf("\nFull Time Employee\nid: %d name: %s age: %d endDate: %s",
		ftEmployee.id,
		ftEmployee.name,
		ftEmployee.age,
		ftEmployee.endDate)
}
func (tEmployee TemporaryEmployee) getMessage() string {
	return fmt.Sprintf("\nTemporary Employee\nid: %d name: %s age: %d taxRate: %d",
		tEmployee.id,
		tEmployee.name,
		tEmployee.age,
		tEmployee.taxRate)
}

En lenguajes como Typescript o Java es necesario definir los métodos de la interfaz para poder implementarla y la implementación es explícita.
En cambio en Go, es suficiente definir los métodos para implementar la interfaz y la implementación es implícita.
En el polimorfismo se utiliza una interfaz (o una clase base) para determinar en tiempo de ejecución el método a utilizar, accediéndolo por medio de la interfaz en vez de hacerlo a través de un objeto en particular, porque en este último caso el método se determina en tiempo de compilación (esto no es polimorfismo).

// no se conoce p hasta el momento en que 
// se ejecuta esta función
// podría ser un FullTimeEmployee
// o un TemporaryEmployee
func getMessage(p PrintInfo) {
  fmt.Println(p.getMessage)
}

// aquí ya se conoce el método a usar
// antes de compilar
ftEmployee.getMessage()

Entonces con polimorfismo, podrías por ejemplo, estar recibiendo desde una BD o un JSON desde un front, los datos de un empleado en tiempo de ejecución y determinar en ese momento cual es el método apropiado para ese empleado en particular.

El tipado de Go respecto de las interfaces es una especie de duck typing. Como regla nmemotécnica: si camina como un pato y hace cuác como un pato, entonces debe ser un pato. La diferencia entre duck typing y el tipado en Go, es que en Go el chequeo sucede en tiempo de compilación, es tipado estático. Es por esto que lo llaman “tipado estructural”, el chequeo le da importancia a la estructura.

Apuntes y código:

  • Diferentes lenguajes de programación utilizan sintaxis explicita para decir que una clase implementa un interfaz
  • Go lo hace de manera implícita lo que permite la re utilización de código y polimorfismo
package main

import "fmt"

type Person struct {
	name string
	age  int
}

type Employee struct {
	id int
}

// De esta manera aplicamos la composicion sobre la herencia
type FullTimeEmployee struct {
	Person
	Employee
	endDate string
}

type TemporatyEmployee struct {
	Person
	Employee
	taxRate int
}
// Creamos la interfaz
type PrintInfo interface {
	getMessage() string
}
// Implementamos la interfaz
func (ftEmployee FullTimeEmployee) getMessage() string {
	return "Full Time Employee"
}

func (tEmployee TemporatyEmployee) getMessage() string {
	return "Temporary Employee"
}
// Creamos el metodo de la interfaz
func getMessage(p PrintInfo) {
	fmt.Println(p.getMessage())
}

func main() {
	ftEmployee := FullTimeEmployee{}
	ftEmployee.name = "Benjamin"
	ftEmployee.age = 20
	ftEmployee.id = 1
	fmt.Printf("%v\n", ftEmployee)
	tEmployee := TemporatyEmployee{}
	getMessage(tEmployee)
	getMessage(ftEmployee)
}

yo no conozco typescript, por lo que me perdi en esta explicación.

Go no implementa interfaces de manera explicita sino de manera implícita.

package main

import "fmt"

type Person struct {
	name string
	age  int
}

type Employee struct {
	id int
}

// Inheritance tipo anonimo
type FullTimeEmployee struct {
	Person
	Employee
	endDate string
}

type TemproaryEmployee struct {
	Person
	Employee
	taxRate float64
}

type PrintInfo interface {
	getMessage() string
}

// Composicion sobre herencia
func (ft FullTimeEmployee) getMessage() string {
	return fmt.Sprintf("Hi %s, you are %d years old. And you are a full time employee", ft.name, ft.age)
}

func (te TemproaryEmployee) getMessage() string {
	return fmt.Sprintf("Hi %s, you are %d years old. And you are a temprary employee", te.name, te.age)
}

func GetMessage(pi PrintInfo) {
	fmt.Println(pi.getMessage())
}

func main() {
	f := FullTimeEmployee{}
	f.name = "John"
	f.age = 30
	f.id = 1
	f.endDate = "2019-01-01"
	fmt.Printf("%+v\n", f)

	t := TemproaryEmployee{}
	t.name = "Mary"
	t.age = 25
	t.id = 2
	t.taxRate = 0.25
	fmt.Printf("%+v\n", t)

	GetMessage(f)
	GetMessage(t)
}

Siento muy forzado go para la OOP, pero siento que se puede mantener la limpieza si todo lo llevamos a un archivo aparte.

Si no queda muy claro lo de interfaces, básicamente es un plano, igual que los structs pero en lugar de enfocarse en los datos (estructura), se enfoca en las acciones (métodos). Ya que no existe implementación en go, al utilizar la interface como parámetro (como el caso de *getMessage*), validará cualquier estructura que cumpla con el plano, es decir, que contenga todos los métodos de la interface.
Comparto mi codigo ```js package main import "fmt" type Person struct { name string age int } type Employee struct { id int } // Composicion de herencia, se agrega el tipo de objeto type FullTimeEmployee struct { Person Employee yearsExperience int } type HalfTimeEmployee struct { Person Employee internship bool } //Constructores func NewFullTimeEmployee(name string, age int, id int, yearExperience int) *FullTimeEmployee { return &FullTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, yearsExperience: yearExperience, } } func (ftEmployee *FullTimeEmployee) GetMesagge() { fmt.Printf("FullTimeEmployee's Name :%s with a age %d and him/her id is %d with %d years of experience\n", ftEmployee.name, ftEmployee.age, ftEmployee.id, ftEmployee.yearsExperience) } func NewHalfTimeEmployee(name string, age int, id int, internship bool) *HalfTimeEmployee { return &HalfTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, internship: internship, } } func (htEmployee *HalfTimeEmployee) GetMesagge() { fmt.Printf("HalfTimeEmployee's Name: %s, Age: %d, ID: %d, Internship: %t\n", htEmployee.name, htEmployee.age, htEmployee.id, htEmployee.internship) } type PrintInfo interface { GetMesagge() } func PrintData(p PrintInfo) { p.GetMesagge() } func main() { ft := NewFullTimeEmployee("Axel", 22, 1, 2) ht := NewHalfTimeEmployee("Alexis", 22, 2, true) PrintData(ft) PrintData(ht) } ```
Comparto mi codigo con implemetacion de constructores con interfacespackage main import "fmt" type Person struct {    name string    age  int} type Employee struct {    id int} // Composicion de herencia, se agrega el tipo de objetotype FullTimeEmployee struct {    Person    Employee    yearsExperience int}type HalfTimeEmployee struct {    Person    Employee    internship bool} //Constructores func NewFullTimeEmployee(name string, age int, id int, yearExperience int) \*FullTimeEmployee {    return \&FullTimeEmployee{        Person: Person{            name: name,            age:  age,        },        Employee: Employee{            id: id,        },        yearsExperience: yearExperience,    }} func (ftEmployee \*FullTimeEmployee) GetMesagge() {    fmt.Printf("FullTimeEmployee's Name :%s  with a age %d and him/her id is %d with %d years of experience\n", ftEmployee.name, ftEmployee.age, ftEmployee.id, ftEmployee.yearsExperience)} func NewHalfTimeEmployee(name string, age int, id int, internship bool) \*HalfTimeEmployee {    return \&HalfTimeEmployee{        Person: Person{            name: name,            age:  age,        },        Employee: Employee{            id: id,        },        internship: internship,    }} func (htEmployee \*HalfTimeEmployee) GetMesagge() {    fmt.Printf("HalfTimeEmployee's Name: %s, Age: %d, ID: %d, Internship: %t\n", htEmployee.name, htEmployee.age, htEmployee.id, htEmployee.internship)} type PrintInfo interface {    GetMesagge()} func PrintData(p PrintInfo) {    p.GetMesagge()} func main() {    ft := NewFullTimeEmployee("Axel", 22, 1, 2)    ht := NewHalfTimeEmployee("Alexis", 22, 2, true)    PrintData(ft)    PrintData(ht)} ```js package main import "fmt" type Person struct { name string age int } type Employee struct { id int } // Composicion de herencia, se agrega el tipo de objeto type FullTimeEmployee struct { Person Employee yearsExperience int } type HalfTimeEmployee struct { Person Employee internship bool } //Constructores func NewFullTimeEmployee(name string, age int, id int, yearExperience int) *FullTimeEmployee { return &FullTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, yearsExperience: yearExperience, } } func (ftEmployee *FullTimeEmployee) GetMesagge() { fmt.Printf("FullTimeEmployee's Name :%s with a age %d and him/her id is %d with %d years of experience\n", ftEmployee.name, ftEmployee.age, ftEmployee.id, ftEmployee.yearsExperience) } func NewHalfTimeEmployee(name string, age int, id int, internship bool) *HalfTimeEmployee { return &HalfTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, internship: internship, } } func (htEmployee *HalfTimeEmployee) GetMesagge() { fmt.Printf("HalfTimeEmployee's Name: %s, Age: %d, ID: %d, Internship: %t\n", htEmployee.name, htEmployee.age, htEmployee.id, htEmployee.internship) } type PrintInfo interface { GetMesagge() } func PrintData(p PrintInfo) { p.GetMesagge() } func main() { ft := NewFullTimeEmployee("Axel", 22, 1, 2) ht := NewHalfTimeEmployee("Alexis", 22, 2, true) PrintData(ft) PrintData(ht) } ```
Hola! Los invito a "jugar" con el siguiente código, ver cómo se comporta solo con el primer método (comentando los últimos 2 SIN comentar las correspondientes llamadas) antes de pasar a interfaces. (Go 1.22.1) ```js package main import "fmt" type Person struct { Name string Age int } type Employee struct { Person // Embedding id int } type Developer struct { Employee // Embedding Language string } func (p *Person) GetBdayMsj() string { return fmt.Sprintf("Hi %s. Congratulations! on your %dth birthday", p.Name, p.Age) } func (e *Employee) GetBdayMsj() string { return fmt.Sprintf("Hi %s. Congratulations! on your %dth birthday. Your employee id is %d", e.Name, e.Age, e.id) } func (d *Developer) GetBdayMsj() string { return fmt.Sprintf("Hi %s. Congratulations! on your %dth birthday. I see you are a %s developer", d.Name, d.Age, d.Language) } func main() { dev := Developer{ Employee: Employee{ Person: Person{ Name: "John", Age: 25, }, id: 1, }, Language: "Go", } fmt.Println(dev.Person.GetBdayMsj()) fmt.Println(dev.Employee.GetBdayMsj()) fmt.Println(dev.GetBdayMsj()) } ```

Genial lo que he podido recordar de TS., pero para quienes no conocen de JS o TS, siento podría ser un poco engorroso tener que ver media clase de un lenguaje al cual no le están apuntando. Por lo demás, muy bien explicado.

package main

import "fmt"

type Person struct {
	name string
	age  uint8
	sex  string
}

type PrintInfo interface {
	getMessage() string
}
type Employee struct {
	id uint64
}

type FullTimeEmployee struct {
	Person
	Employee
	endTime int
}

func (ftEmployee FullTimeEmployee) getMessage() string {
	return "Este es un fulltimeEmployee"
}

type TemporaryEmployee struct {
	Person
	Employee
	taxRate string
}

func (tEmployee TemporaryEmployee) getMessage() string {
	return "Este es un TemporaryEmployee"
}

func getMessage(p PrintInfo) {
	fmt.Printf("%+v\n", p.getMessage())
}

func main() {
	ftEmployee := FullTimeEmployee{}
	ftEmployee.name = "Carlos"
	ftEmployee.age = 25
	ftEmployee.id = 1
	ftEmployee.sex = "hombre"
	fmt.Printf("%+v\n", ftEmployee)

	tEmployee := TemporaryEmployee{}
	fmt.Printf("%+v\n", tEmployee)

	getMessage(ftEmployee)
	getMessage(tEmployee)

}

codigo:

package main

import "fmt"

type Person struct {
	name string
	age  int
}

type Employee struct {
	id int
}

type FullTimeEployee struct {
	Person
	Employee
	endDate string
}

// metodo de FullTimeEmployee
func (ftEmploye FullTimeEployee) getMessage() string {
	return "Full Time Employe"
}

type TemporaryEmployee struct {
	Person
	Employee
	taxRate int
}

// Metodo de Temporary Employee
func (tEmploye TemporaryEmployee) getMessage() string {
	return "Temporary Employe"
}

// Interface
type PrintInfo interface {
	getMessage() string
}

func getMessage(p PrintInfo) {
	fmt.Println(p.getMessage())
}

func main() {
	ftEmployee := FullTimeEployee{}
	ftEmployee.id = 1
	ftEmployee.name = "Maria"
	ftEmployee.age = 27
	//fmt.Printf("%v", ftEmployee)

	tEmployee := TemporaryEmployee{}
	getMessage(ftEmployee)
	getMessage(tEmployee)

}