Go: Programación Orientada a Objetos

Golang ofrece Programación Orientada a Objetos mediante estructuras y métodos. Aprende a implementar conceptos OOP en Go con ejemplos prácticos y recomendaciones actuales.

Aprende Go GRATIS y certifícate

La Programación Orientada a Objetos (POO) es un paradigma que permite estructurar el código en torno a objetos que representan entidades del mundo real. Aunque Golang no es un lenguaje puramente orientado a objetos, ofrece características que permiten aplicar conceptos de POO de manera eficiente. A continuación, exploraremos cómo implementar estos conceptos en Go.

Estructuras y Métodos

En Golang, las estructuras (structs) son la base para definir tipos personalizados que pueden tener métodos asociados.

Definición de Structs

Una estructura en Go se define utilizando la palabra clave type seguida del nombre y la palabra clave struct.

type Usuario struct {
    Nombre   string
    Apellido string
    Edad     int
}

En este ejemplo, Usuario es una estructura que contiene tres campos: Nombre, Apellido y Edad.

Métodos Asociados a Structs

Podemos asociar métodos a una estructura para definir comportamientos.

func (u *Usuario) NombreCompleto() string {
    return u.Nombre + " " + u.Apellido
}

El método NombreCompleto devuelve el nombre completo del usuario concatenando el nombre y el apellido.

Encapsulamiento

El encapsulamiento se logra controlando el acceso a los campos y métodos de las estructuras.

Exportación de Campos y Métodos

En Go, los identificadores que comienzan con mayúscula son exportados (públicos), mientras que los que comienzan con minúscula son no exportados (privados).

type cuentaBancaria struct {
    saldo float64
}

func (c *cuentaBancaria) Depositar(monto float64) {
    c.saldo += monto
}

func (c *cuentaBancaria) obtenerSaldo() float64 {
    return c.saldo
}

En este caso, cuentaBancaria y sus métodos son no exportados, lo que significa que solo son accesibles dentro del mismo paquete.

Herencia mediante Composición

Golang no soporta la herencia clásica, pero se puede lograr una reutilización de código similar mediante la composición.

Composición de Structs

Podemos incrustar una estructura dentro de otra para reutilizar sus campos y métodos.

type Empleado struct {
    Usuario
    ID       int
    Posicion string
}

func (e *Empleado) Detalles() string {
    return fmt.Sprintf("Empleado: %s, ID: %d, Posición: %s", e.NombreCompleto(), e.ID, e.Posicion)
}

Aquí, Empleado incluye la estructura Usuario, heredando sus campos y métodos.

Interfaces

Las interfaces en Golang definen un conjunto de métodos que un tipo debe implementar.

Definición de Interfaces

type Describible interface {
    Describir() string
}

Cualquier tipo que implemente el método Describir cumple con la interfaz Describible.

Implementación de Interfaces

func (u Usuario) Describir() string {
    return fmt.Sprintf("Usuario: %s, Edad: %d", u.NombreCompleto(), u.Edad)
}

Ahora, Usuario implementa Describible al tener el método Describir.

Uso de Interfaces

func MostrarDescripcion(d Describible) {
    fmt.Println(d.Describir())
}

Podemos pasar cualquier tipo que implemente Describible a la función MostrarDescripcion.

Polimorfismo

El polimorfismo en Go se logra mediante el uso de interfaces, permitiendo que diferentes tipos sean tratados de la misma manera si implementan los mismos métodos.

Ejemplo de Polimorfismo

type Producto struct {
    Nombre string
    Precio float64
}

func (p Producto) Describir() string {
    return fmt.Sprintf("Producto: %s, Precio: %.2f", p.Nombre, p.Precio)
}

// Uso polimórfico
func main() {
    usuario := Usuario{Nombre: "Ana", Apellido: "García", Edad: 30}
    producto := Producto{Nombre: "Laptop", Precio: 999.99}

    MostrarDescripcion(usuario)
    MostrarDescripcion(producto)
}

Tanto Usuario como Producto implementan Describir, por lo que pueden ser utilizados de manera intercambiable en MostrarDescripcion.

Emulación de Clases y Objetos

Aunque Go no tiene clases, las estructuras y métodos permiten emular su comportamiento.

Creación de Instancias

u := Usuario{Nombre: "Carlos", Apellido: "Pérez", Edad: 25}

Creamos una instancia de Usuario inicializando sus campos.

Modificación de Campos

u.Edad = 26

Podemos acceder y modificar los campos exportados directamente.

Embedding y Delegación

El embedding permite incluir funcionalidades de un tipo dentro de otro sin herencia.

Embedding de Interfaces

type Notificador interface {
    Notificar(mensaje string) error
}

type UsuarioNotificador struct {
    Usuario
    Notificador
}

En este ejemplo, UsuarioNotificador incluye Usuario y cualquier implementación de Notificador.

Implementación Personalizada

Podemos proporcionar una implementación personalizada de Notificar.

type EmailNotificador struct {
    Email string
}

func (e EmailNotificador) Notificar(mensaje string) error {
    // Lógica para enviar un correo electrónico
    return nil
}

Y utilizarlo:

un := UsuarioNotificador{
    Usuario:    u,
    Notificador: EmailNotificador{Email: "carlos@example.com"},
}

un.Notificar("Bienvenido a Golang")

Constructores en Golang

Aunque Go no tiene constructores tradicionales, es común utilizar funciones para inicializar estructuras.

Función Constructora

func NuevoUsuario(nombre, apellido string, edad int) *Usuario {
    return &Usuario{Nombre: nombre, Apellido: apellido, Edad: edad}
}

// Uso
u := NuevoUsuario("Laura", "Méndez", 28)

Esta función devuelve un puntero a una nueva instancia de Usuario.

Métodos Receptores: Valor vs. Puntero

Al definir métodos, podemos utilizar receptores por valor o por puntero.

Receptores por Valor

func (u Usuario) MostrarEdad() int {
    return u.Edad
}

Los cambios en u dentro del método no afectan a la instancia original.

Receptores por Puntero

func (u *Usuario) IncrementarEdad() {
    u.Edad++
}

Los cambios realizados en u afectan a la instancia original, ya que se está trabajando con su dirección de memoria.

Sobrescritura y Sobrecarga

Go no soporta la sobrescritura de métodos ni la sobrecarga de funciones como en otros lenguajes orientados a objetos.

Alternativas en Go

En lugar de sobrecarga, podemos utilizar variádicos o interfaces vacías.

func Sumar(numeros ...int) int {
    total := 0
    for _, n := range numeros {
        total += n
    }
    return total
}

// Uso
resultado := Sumar(1, 2, 3, 4)

Abstracción

La abstracción se logra mediante el diseño cuidadoso de interfaces y estructuras que oculten los detalles de implementación.

Ejemplo de Abstracción

type Almacenamiento interface {
    Guardar(datos string) error
}

type baseDeDatos struct{}

func (db baseDeDatos) Guardar(datos string) error {
    // Lógica para guardar en la base de datos
    return nil
}

// Uso
func Procesar(almacen Almacenamiento, datos string) {
    almacen.Guardar(datos)
}

El usuario de Procesar no necesita conocer cómo Guardar implementa el almacenamiento de datos.

Ejemplos Prácticos

Implementación de una Calculadora Simple

type Calculadora struct {
    Resultado float64
}

func (c *Calculadora) Sumar(valor float64) {
    c.Resultado += valor
}

func (c *Calculadora) Restar(valor float64) {
    c.Resultado -= valor
}

func (c *Calculadora) Multiplicar(valor float64) {
    c.Resultado *= valor
}

func (c *Calculadora) Dividir(valor float64) {
    if valor != 0 {
        c.Resultado /= valor
    }
}

Uso de la Calculadora

calc := Calculadora{}
calc.Sumar(10)
calc.Multiplicar(2)
fmt.Println("Resultado:", calc.Resultado) // Resultado: 20

Conclusión

Golang proporciona herramientas efectivas para aplicar los principios de la Programación Orientada a Objetos. A través de estructuras, métodos e interfaces, es posible construir programas robustos y mantenibles. Aunque Go adopta un enfoque diferente al de los lenguajes orientados a objetos tradicionales, su diseño sencillo y eficiente permite implementar conceptos de POO de manera clara y concisa.

Empezar curso de Go

Lecciones de este módulo de Go

Lecciones de programación del módulo Programación Orientada a Objetos del curso de Go.

Ejercicios de programación en este módulo de Go

Evalúa tus conocimientos en Programación Orientada a Objetos con ejercicios de programación Programación Orientada a Objetos de tipo Test, Puzzle, Código y Proyecto con VSCode.