Introducción

1

Programación Orientada a Objetos y Concurrencia en Go

2

Concurrencia y Testing en Go: Go Routinas y Worker Pools

3

Fundamentos de Programación en Go: Structs, Apuntadores y Manejo de Errores

4

Go Routines, Canales y Apuntadores en Golang

Programación orientada a objetos

5

Programación Orientada a Objetos en Go: Análisis y Comparativa

6

Equivalente de Clases en Go: Uso de Structs y Propiedades

7

Métodos en Structs: Implementación de Receiver Functions en Go

8

Implementación de Constructores en Go: Ejemplos y Mejores Prácticas

9

Herencia y Composición en Programación Orientada a Objetos

10

Interfaces y Polimorfismo en TypeScript y Go

11

Patrón Abstract Factory: Interfaces y Polimorfismo en Go

12

Implementación del Patrón Factory en Go para Notificaciones

13

Funciones Anónimas en Go: Uso y Consideraciones Prácticas

14

Funciones Variádicas y Retornos con Nombre en Go

Go Modules

15

Manejo de Dependencias y Módulos en Go

16

Creación y Publicación de Módulos en GitHub con Go

Testing

17

Tests unitarios y code coverage en Go

18

Cobertura de Código y Testing en Go: Mejorando la Calidad del Software

19

Profiling de Código en Go para Optimización de Rendimiento

20

Testing Unitario en Go: Uso de Mock Services

21

Testeo Unitario de Funciones con MOOC en Go

Concurrencia

22

Canales con y sin buffer en Go: diferencias y uso práctico

23

Sincronización de Rutinas en Go con Wait Group

24

"Uso de Canales con Buffer como Semáforos en Go"

25

Manejo de Canales de Lectura y Escritura en Go

26

Concurrencia en Go: Creación de Worker Pools y Fibonacci

27

Multiplexación de Canales en Go con Select y Case

Proyecto: servidor con worker pools

28

Creación de un Web Server con Worker Pool en Go

29

Creación de un Servidor Web Concurrente con Go

Conclusión

30

Resumen del Curso Avanzado de Go: Módulos, Tests y Concurrencia

No tienes acceso a esta clase

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

Herencia y Composición en Programación Orientada a Objetos

9/30
Recursos

¿Qué es la herencia en la programación orientada a objetos?

La herencia es un concepto central en la programación orientada a objetos (POO) que permite a las clases derivar propiedades y comportamientos de otras clases. Esto resulta en una reutilización significativa del código y facilita el polimorfismo. Sin embargo, puede generar controversia debido a cómo distintos lenguajes de programación implementan jerarquías de clases y cómo estas pueden ser fácilmente alteradas, afectando el código dependiente.

El uso de la herencia lleva a la creación de una clase base que contiene las propiedades comunes, de la que otras clases pueden derivar. Estas son llamadas clases derivadas y heredan propiedades y comportamientos, lo que reduce la necesidad de duplicar código.

¿Cómo se implementa la herencia con TypeScript?

En TypeScript, la herencia se implementa utilizando la palabra reservada extends. Al hacerlo, una clase derivada puede heredar propiedades y métodos de una clase base:

class BaseEmployee {
    // propiedades y métodos comunes
}

class TemporaryEmployee extends BaseEmployee {
    // propiedades y métodos específicos
}

class FullTimeEmployee extends BaseEmployee {
    // propiedades y métodos específicos
}

En este ejemplo, TemporaryEmployee y FullTimeEmployee heredan de BaseEmployee, lo que significa que estas clases tendrán acceso a sus métodos y propiedades públicas. Este es un ejemplo claro de cómo la herencia ayuda a evitar la duplicación de código.

¿Qué desafíos presenta la herencia?

Aunque es muy útil, la herencia puede llevar a situaciones donde las clases derivadas terminan sobrescribiendo métodos de la clase base. Esto puede indicar un mal uso de la herencia y sugiere que una composición podría ser más apropiada.

Además, no todos los lenguajes de programación abordan la herencia de la misma manera. Por ejemplo, en Go, el concepto de herencia tradicional no existe, y en su lugar se recomienda el principio de "composición sobre la herencia". La composición permite que una clase tenga propiedades de otra sin necesariamente ser un tipo de esa clase.

¿Cómo se aplica la composición en Go?

En Go, la composición se logra usando "campos anónimos". En lugar de heredar de una clase base, una estructura (struct) contiene campos que representan las propiedades de otras estructuras:

type Person struct {
    Name string
    Age int
}

type Employee struct {
    ID int
}

type FullTimeEmployee struct {
    Person
    Employee
}

En este ejemplo, FullTimeEmployee tiene tanto las características de Person como de Employee. No declaramos explícitamente que FullTimeEmployee es un Person o Employee, así conseguimos el beneficio de ambas sin heredar.

¿Cómo afecta el polimorfismo?

El polimorfismo es otro concepto importante en POO. En TypeScript, es sencillo implementar polimorfismo porque una función puede aceptar una clase base como argumento, permitiendo que cualquier clase derivada sea tratada como esa clase base.

function printMessage(employee: BaseEmployee) {
    console.log(employee.getMessage());
}

En Go, esto no es tan directo debido a la falta de herencia. Aunque la composición resuelve muchos problemas de diseño, limita el uso de polimorfismo tradicional. Sin embargo, utilizando interfaces, Go ofrece un enfoque diferente para lograr un comportamiento similar, permitiendo a estructuras compartir métodos sin necesidad de herencia directa.

La discusión en torno a la herencia y la composición destaca la importancia de elegir correctamente cómo estructurar el código. Ambas técnicas tienen sus pros y contras, y su uso debe basarse en las necesidades particulares del proyecto y del lenguaje de programación.

Aportes 18

Preguntas 2

Ordenar por:

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

Go no permite la herencia, go utiliza la composicion.
la composicion, a diferencia de la herencia, no es una clase hija de… sino que contiene los metodos de las clases indicadas.

codigo:

package main

import "fmt"

type Person struct {
	name string
	age  int
}

type Employee struct {
	id int
}

type FullTimeEployee struct {
	Person
	Employee
}

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

La siguiente función es un método para que FullTimeEmployee imprima sus atributos de forma más ✨ bonita ✨:

func (employee FullTimeEmployee) String() string {
	return fmt.Sprintf("\nid: %d, name: %s, age: %d ", employee.id, employee.name, employee.age)
}
  • No entiendo por que usa otros lenguajes para explicar, explica todo en Golang, vengo aprender GO, no typescript, para eso me voy al curso de typescript, Entiendo que quiere explicar las diferencias, pero cuando yo explico python no voy con ejemplos de Java, se confunde la gente y se enredan mas, los primeros 7min es de typescript, increible

Cuando se tienen receiver functions asociadas a un struct y se forma otro struct a partir de este, los métodos también funcionan con el nuevo struct.
Creo que esto se parece más a la herencia en Go hablando de métodos.
Y aún contando con esa herencia de métodos puedo reescribir la receiver function en el nuevo struct solo creando una función con el mismo nombre y recibiendo el struct. Así tendría polimorfismo.

package main

import "fmt"

type Person struct {
	Name string
	Age int
}

type Employee struct {
	Person
	Position string
	Salary int
}

func (p Person) SayHello() {
	fmt.Printf("Hi, my name is %s\n", p.Name)
}

func main() {
	me := Person{Name: "Edwin", Age: 30}
	developer := Employee{
		Person: Person{Name: "Lorena", Age: 27},
		Position: "Developer",
		Salary: 2000,
	}

	person.SayHello() // Hi, my name is Edwin
	developer.SayHello() // Hi, my name is Lorena
}

Ahora reescribiendo el método SayHello

package main

import "fmt"

type Person struct {
	Name string
	Age int
}

type Employee struct {
	Person
	Position string
	Salary int
}

func (p Person) SayHello() {
	fmt.Printf("Hi, my name is %s\n", p.Name)
}

func (e Employee) SayHello() {
	fmt.Printf("Hi, I am a %s and my name is %s\n", e.Position,  e.Name)
}

func main() {
	me := Person{Name: "Edwin", Age: 30}
	developer := Employee{
		Person: Person{Name: "Lorena", Age: 27},
		Position: "Developer",
		Salary: 2000,
	}

	person.SayHello() // Hi, my name is Edwin
	developer.SayHello() // Hi, I am a developer and my name is Lorena
}

Podemos usar esta funcion como un constructor

func CreateFullTimeEmployee(name string, age int, id int) *FullTimeEmployee {
	newEmployee := FullTimeEmployee{}
	newEmployee.name = name
	newEmployee.age = age
	newEmployee.id = id

	return &newEmployee
}
func main() {
	ftEmployee := CreateFullTimeEmployee("Alex", 20, 0)
	fmt.Println(*ftEmployee)
}

definitivamente platzi es horrible para la teoria…

De hecho en el ejemplo la función que acepta una persona, si se puede ocupar, por el empleado a tiempo completa. ya que Persona es parte de FullTimeEmployee, simplemente hay que usar persona de FullTimeEmployee.

package main

import (
	"fmt"
)

type Person struct {
	name string
	age  int
}

type Employee struct {
	id int
}
type FullTimeEmployee struct {
	Person
	Employee
}

func GetMessage(p Person) {
	fmt.Printf("%s, tu edad es %d", p.name, p.age)
}

func main() {
	var ftEmployee FullTimeEmployee
	ftEmployee.age = 36
	ftEmployee.id = 1233
	ftEmployee.name = "Jose"
	GetMessage(ftEmployee.Person)
}```
package main

import "fmt"

type Person struct {
	name string
	age  int
}

type Employee struct {
	id int
}

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

// Composicion sobre herencia
func GetMessage(p Person) string {
	return fmt.Sprintf("Hi %s, you are %d years old", p.name, p.age)
}

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

	fmt.Println(GetMessage(f))
}

Aqui implemete un cosntructor y un metodo que regresa un informacion ```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 } //Metodo func NewFullTimeEmployee(name string, age int, id int) *FullTimeEmployee { return &FullTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, } } func (ftEmployee *FullTimeEmployee) GetMesagge() { fmt.Printf("FullTimeEmployee's Name :%s with a age %d and him/her id is %d\n", ftEmployee.name, ftEmployee.age, ftEmployee.id) } func main() { ft := NewFullTimeEmployee("Axel", 22, 1) ft.GetMesagge() } ```
Aqui implemete un constructor y un metodo donde regresa informacion ```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 } //Metodo func NewFullTimeEmployee(name string, age int, id int) *FullTimeEmployee { return &FullTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, } } func (ftEmployee *FullTimeEmployee) GetMesagge() { fmt.Printf("FullTimeEmployee's Name :%s with a age %d and him/her id is %d\n", ftEmployee.name, ftEmployee.age, ftEmployee.id) } func main() { ft := NewFullTimeEmployee("Axel", 22, 1) ft.GetMesagge() } ```package 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} //Metodo func NewFullTimeEmployee(name string, age int, id int) \*FullTimeEmployee {    return \&FullTimeEmployee{        Person: Person{            name: name,            age:  age,        },        Employee: Employee{            id: id,        },    }} func (ftEmployee \*FullTimeEmployee) GetMesagge() {    fmt.Printf("FullTimeEmployee's Name :%s  with a age %d and him/her id is %d\n", ftEmployee.name, ftEmployee.age, ftEmployee.id)}func main() {    ft := NewFullTimeEmployee("Axel", 22, 1)    ft.GetMesagge()}
Aqui implemete el constructor y un metodopackage 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} //Metodo func NewFullTimeEmployee(name string, age int, id int) \*FullTimeEmployee {    return \&FullTimeEmployee{        Person: Person{            name: name,            age:  age,        },        Employee: Employee{            id: id,        },    }} func (ftEmployee \*FullTimeEmployee) GetMesagge() {    fmt.Printf("FullTimeEmployee's Name :%s  with a age %d and him/her id is %d\n", ftEmployee.name, ftEmployee.age, ftEmployee.id)}func main() {    ft := NewFullTimeEmployee("Axel", 22, 1)    ft.GetMesagge()} ```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 } //Metodo func NewFullTimeEmployee(name string, age int, id int) *FullTimeEmployee { return &FullTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, } } func (ftEmployee *FullTimeEmployee) GetMesagge() { fmt.Printf("FullTimeEmployee's Name :%s with a age %d and him/her id is %d\n", ftEmployee.name, ftEmployee.age, ftEmployee.id) } func main() { ft := NewFullTimeEmployee("Axel", 22, 1) ft.GetMesagge() } ```
Aqui implemente el constructor ```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 } func newFullTimeEmployee(name string, age int, id int) *FullTimeEmployee { return &FullTimeEmployee{ Person: Person{ name: name, age: age, }, Employee: Employee{ id: id, }, } } func main() { ft := newFullTimeEmployee("Axel", 22, 1) fmt.Println(*ft) } ```package 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} func newFullTimeEmployee(name string, age int, id int) \*FullTimeEmployee {    return \&FullTimeEmployee{        Person: Person{            name: name,            age:  age,        },        Employee: Employee{            id: id,        },    }} func main() {    ft := newFullTimeEmployee("Axel", 22, 1)    fmt.Println(\*ft)}

Yo pienso que herencia es un problema de diseño, aunque parece fácil en sintaxis para el programador.
Ejemplo, si escribiéramos el código ej. de java que usa herencia en C y tuviéramos que implementar la herencia a mano, podemos ver la magnitud de la ineficiencia de usar herencia muy fácil, tendríamos 2000 punteros, punteros de punteros que apuntan a punteros, mallocs/free’s de memoria y uniones
Al usar composición y agregación se evitan esos problemas desde el principio y esto está disponible en todos los lenguajes, incluso Orientados a objetos.

Excelente clase!!! Muy clara la distinción entre la herencia clásica en otros lenguajes y la implementación que realiza golang por medio de la composición.

herencia: la clase hija ES la clase padre
composición: la clase hija TIENE la(s) clase(s) padre

package main

import "fmt"

type Person struct {
	name string
	age  uint8
	sex  string
}

type Employee struct {
	id uint64
}

type FullTimeEmployee struct {
	Person
	Employee
}

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

Les dejo lo que seria el método constructor de FullTimeEmployee:

func newFullTimeEmployee(name string, age int, id int) *FullTimeEmployee {
	return &FullTimeEmployee{
		Person{Name: name, Age: age},
		Employee{Id: id},
	}
}

El ejemplo final se puede llegar a hacer pero pasando de que struct viene dicho dato. Ejemplo

func GetMessage(p Person, e Employee) {
	fmt.Printf("El Id es %d %s with age %d\n", e.id, p.name, p.age)
}
func main () {
GetMessage(ftEmployee.Person, ftEmployee.Employee)
}

Para hacer funcionar el método GetMessage, he probado que pueden pasar la persona que corresponde a ese un empleado, de esta forma:

GetMessage(ftEmployee.Person)