Orientación a Objetos en Go
Tags: go, poo, oop, softwareengineering
Published: 2024-01-03 19:05:47

¿Es Go un lenguaje orientado a objetos? ¿Es posible pober en practica la orientación a objetos en Go?

# ¿Qué es la POO?
En palabras simples: Paradigma de programación que se basa en el concepto de **objetos**. Un objeto es una **entidad** que tiene un estado y un **comportamiento**. El **estado** de un **objeto** se representa mediante sus **atributos**, mientras que su **comportamiento** se representa mediante sus **métodos**.

La **POO** permite dividir el programa en unidades más pequeñas con el fin de facilitar el desarrollo, mantenimiento y reutilización. Ademas de poder modelar el mundo real de manera más natural.

# Ventajas y Desventajas de la POO

## Ventajas
- Reutilización de código: La POO permite reutilizar el código de las clases ya existentes, lo que ahorra tiempo y esfuerzo de desarrollo.
- Claridad y legibilidad del código: La POO ayuda a organizar el código de una manera clara y legible, lo que facilita su comprensión y mantenimiento.
- Escalabilidad: La POO permite crear sistemas más complejos y escalables.
- Agilidad: La POO ayuda a agilizar el desarrollo de software, ya que permite dividir el trabajo en unidades más pequeñas y fáciles de manejar.

## Desventajas
- Complejidad: La POO puede ser un paradigma complejo de aprender y dominar.
- Costo: La POO requiere un mayor esfuerzo de desarrollo que otros paradigmas de programación.

# Conceptos Principales de la POO
- Abstracción: Representación de la realidad en forma de clases y objetos.
  - Clase: Es un molde o plantilla para crear objetos. Una clase define los atributos y métodos de un objeto.
  - Objeto: Es una instancia de una clase. Un objeto tiene un estado y un comportamiento que están definidos por la clase de la que es instancia.
  - Atributo: Es una variable que representa una característica o propiedad de un objeto.
  - Método: Es una función que representa una acción que puede realizar un objeto.
- Encapsulamiento: Es el ocultamiento de los detalles de implementación de un objeto.
- Herencia: Es la capacidad de un objeto para heredar los atributos y métodos de otro objeto.
- Polimorfismo: Es la capacidad de un objeto para comportarse de forma diferente en función del contexto.

# Como se Aplican los Conceptos en Go
- Abstracción
  - Clases: En Go, las clases pasan a ser las structs. Las structs son tipos de datos que pueden contener atributos y métodos.
  - Objetos: En Go, los objetos son instancias de las structs.
  - Atributos: En Go, los atributos son las variables declaradas en las structs.
  - Métodos: En Go, los métodos son funciones que se declaran dentro de las structs.
- Encapsulamiento: En Go, el encapsulamiento se logra utilizando los modificadores de acceso (como cuando el nombre de una variable o método empieza con la primera letra a mayúscula o minúscula).
- Herencia: En Go, la herencia no se admite directamente. Sin embargo, se puede lograr mediante:
  - Composición: Structs dentro de otros structs.
  - Interfaces: Declarando las func que necesitamos heredar y posteriormente implementado structs que cumplan con la interfaz
- Polimorfismo: En Go, el polimorfismo se logra mediante la interfaz. Las interfaces definen un conjunto de métodos que deben implementarse. De esta forma, varios structs pueden cumplir con los requisitos de la interfaz.

# POO en Go en la Practica
Podemos representar la orientación a objetos en alto nivel con los siguientes dibujos:
![Representación grafica de una entidad](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tiwzjhllk8jpuyne7zos.png)

## Abstracción (Entidades u Objetos)
Por lo anterior podemos deducir de los objetos como tal lo siguiente: a grandes rasgos y a primera vista, la gran diferencia está entre las structs y las clases. Esto en la practica esta diferencia sería:
- Otros lenguajes: Se declara el comportamiento del objeto dentro de su misma estructura de clases.
- Go: Su comportamiento se asocia a una struct en particular.

Para el siguiente ejemplo, definamos un objeto persona que tiene como atributos un Nombre y una Edad, ademas que como comportamiento pueda Saludar.

### C Sharp
```csharp
class Persona
{
    public string Nombre { get; set; }
    public int Edad { get; set; }

    public void Saludar()
    {
        Console.WriteLine($"Hola, soy {Nombre} y tengo {Edad} años.");
    }
}
```

### Python
```python
class Persona:
    def __init__(self, nombre, edad):
        self.Nombre = nombre
        self.Edad = edad

    def Saludar(self):
        print(f"Hola, soy {self.Nombre} y tengo {self.Edad} años.")
```

### Go
```go
type Persona struct {
    Nombre string
    Edad   int
}

func (p Persona) Saludar() {
    fmt.Printf("Hola, soy %s y tengo %d años.\n", p.Nombre, p.Edad)
}
```
Para todos estos ejemplos, el objeto persona será el siguiente:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwo31is3ask5t477mq2a.png)

## Encapsulamiento
Mientras que en otros lenguajes podemos encapsular un atributo o un método mediante la palabra reservada “private”, en lenguajes como Go o Python se hace mediante convenciones. En el caso de Go basta solo con escribir la primera letra de un método o atributo en minúscula.
### C Sharp

```csharp
class Persona
{
    private string Nombre { get; set; }
    private int Edad { get; set; }

    private void Saludar()
    {
        Console.WriteLine($"Hola, soy {Nombre} y tengo {Edad} años.");
    }
}
```

### Python
```python
class Persona:
    def __init__(self, nombre, edad):
        self._Nombre = nombre
        self._Edad = edad

    def _Saludar(self):
        print(f"Hola, soy {self._Nombre} y tengo {self._Edad} años.")
```

### Go
```go
type Persona struct {
    nombre string
    edad   int
}

func (p Persona) saludar() {
    fmt.Printf("Hola, soy %s y tengo %d años.\n", p.nombre, p.edad)
}
```

## Composición y Herencia
En Go no existe algo tal como las herencias, para esto podemos usar la composición para incorporar una Struct dentro de otra.

### C Sharp
```csharp
class Persona
{
    private string Nombre { get; set; }
    private int Edad { get; set; }

    private void Saludar()
    {
        Console.WriteLine($"Hola, soy {Nombre} y tengo {Edad} años.");
    }
}

class Trabajador : Persona
{
  private int ID { get; set; }
}
```

### Python
```python
class Persona:
    def __init__(self, nombre, edad):
        self._Nombre = nombre
        self._Edad = edad

    def _Saludar(self):
        print(f"Hola, soy {self._Nombre} y tengo {self._Edad} años.")

class Trabajador(Persona):
  pass
```

### Go
```go
type Persona struct {
    Nombre string
    Edad   int
}

type Trabajador struct {
  Persona
  ID      int
}

func (p Persona) Saludar() {
    fmt.Printf("Hola, soy %s y tengo %d años.\n", p.Nombre, p.Edad)
}
```

## Polimorfismo
El polimorfismo en Go se puede hacer a travez de interfaces. Estas interfaces pueden ser implementadas por cualquier struct que cumpla con sus condiciones.

### C Sharp
```csharp
using System;

// Clase base (Superclase)
class Animal
{
  public virtual void HacerSonido()
  {
    Console.WriteLine("El animal hacer un sonido");
  }
}

// Clase derivada
class Perro : Animal
{
  public override void HacerSonido()
  {
    Console.WriteLine("El perro ladra");
  }
}

// Clase derivada
class Gato : Animal
{
  public override void HacerSonido()
  {
    Console.WriteLine("El gato maulla");
  }
}

class Program
{
  static void Main
  {
    // Crea objetos de diferentes clases
    Animal MiAnimal = new Animal();
    Animal MiPerro = new Perro();
    Animal MiGato = new Gato();

    MiAnimal.HacerSonido(); // "El animal hacer un sonido"
    MiPerro.HacerSonido(); // "El perro ladra"
    MiGato.HacerSonido(); // "El gato maulla"
  }
}
```

### Python
```python
# Clase base
class Animal:
  def hacer_sonido(self):
    pass

# Clase derivada
class Perro(Animal):
  def hacer_sonido(self):
    return "El perro ladra"

# Clase derivada
class Gato(Animal):
  def hacer_sonido(self):
    return "El gato maulla"

# Función que utiliza polimorfismo
def hacer_sonido_animal(animal):
  return animal.hacer_sonido()

# Crea objetos de diferentes clases
mi_animal = Animal()
mi_perro = Perro()
mi_gato = Gato()

print(hacer_sonido_animal(mi_animal)) # No imprime nada
print(hacer_sonido_animal(mi_perro)) # "El perro ladra"
print(hacer_sonido_animal(mi_gato)) # "El gato maulla"
```

### Go
```go
package main

import "fmt"

type Animal interface {
  HacerSonido() string
}

type Perro struct{}

func (p Perro) HacerSonido() string {
  return "El perro ladra"
}

type Gato struct{}

func (g Gato) HacerSonido() string {
  return "El gato maulla"
}

func HacerSonidoAnimal(a Animal) string {
  fmt.Println(a.HacerSonido())
}

func Main() {
  miPerro := Perro{}
  miGato := Gato{}

  HacerSonidoAnimal(miPerro) // "El perro ladra"
  HacerSonidoAnimal(miGato) // "El gato maulla"
}
```
Recordar que las interfaces son satisfacidas solo si el struct que se implementa tiene las mismas func que la interfaz.

# En Conclusión
Go no es un lenguaje orientado a objetos tradicional, pero si se puede trabajar con la orientación a objetos. Como pudimos ver en las secciones anteriores, podemos adaptar los conceptos principales de la POO con las herramientas que nos entrega Go.