Learn-Software.com

Abstracción

Los aspectos clave de la abstracción incluyen:

  1. Simplificación: La abstracción reduce la complejidad ocultando detalles innecesarios.
  2. Enfoque en características esenciales: Enfatiza lo que hace un objeto en lugar de cómo lo hace.
  3. Separación de preocupaciones: Permite separar la interfaz de una clase de su implementación.
  4. Modularidad: La abstracción promueve el diseño modular al definir límites claros entre componentes.

Clases abstractas e interfaces

En muchos lenguajes orientados a objetos, la abstracción se implementa a través de clases abstractas e interfaces. Aunque Python no tiene un concepto integrado de interfaz, podemos lograr una funcionalidad similar usando clases base abstractas. El módulo abc de Python proporciona infraestructura para definir clases base abstractas:

from abc import ABC, abstractmethod

class Figura(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimetro(self):
        pass

class Rectangulo(Figura):
    def __init__(self, ancho, alto):
        self.ancho = ancho
        self.alto = alto

    def area(self):
        return self.ancho * self.alto

    def perimetro(self):
        return 2 * (self.ancho + self.alto)

class Circulo(Figura):
    def __init__(self, radio):
        self.radio = radio

    def area(self):
        return 3.14159 * self.radio ** 2

    def perimetro(self):
        return 2 * 3.14159 * self.radio

# Uso
# figuras = [Figura()]  # Esto generaría TypeError
figuras = [Rectangulo(5, 4), Circulo(3)]

for figura in figuras:
    print(f"Área: {figura.area()}, Perímetro: {figura.perimetro()}")

# Salida:
# Área: 20, Perímetro: 18
# Área: 28.27431, Perímetro: 18.84954

En este ejemplo:

Implementando abstracción en Python

Aunque Python proporciona clases base abstractas para definir interfaces formalmente, también podemos lograr la abstracción mediante convenciones y documentación. Veamos un ejemplo sin utilizar ABC:

class BaseDeDatos:
    def conectar(self):
        raise NotImplementedError("La subclase debe implementar este método abstracto")

    def ejecutar(self, consulta):
        raise NotImplementedError("La subclase debe implementar este método abstracto")

class BaseDeDatosMySQL(BaseDeDatos):
    def conectar(self):
        print("Conectando a la base de datos MySQL...")

    def ejecutar(self, consulta):
        print(f"Ejecutando consulta MySQL: {consulta}")

class BaseDeDatosPostgreSQL(BaseDeDatos):
    def conectar(self):
        print("Conectando a la base de datos PostgreSQL...")

    def ejecutar(self, consulta):
        print(f"Ejecutando consulta PostgreSQL: {consulta}")

def realizar_operacion_en_bd(base_de_datos):
    base_de_datos.conectar()
    base_de_datos.ejecutar("SELECT * FROM usuarios")

# Uso
bd_mysql = BaseDeDatosMySQL()
bd_postgres = BaseDeDatosPostgreSQL()

realizar_operacion_en_bd(bd_mysql)
realizar_operacion_en_bd(bd_postgres)

# Salida:
# Conectando a la base de datos MySQL...
# Ejecutando consulta MySQL: SELECT * FROM usuarios
# Conectando a la base de datos PostgreSQL...
# Ejecutando consulta PostgreSQL: SELECT * FROM usuarios

En este ejemplo:

Principios de diseño y patrones

La abstracción es un componente clave de varios principios y patrones de diseño importantes:

  1. Principios SOLID:
    • Principio de Responsabilidad Única (SRP).
    • Principio de Abierto/Cerrado (OCP).
    • Principio de Sustitución de Liskov (LSP).
    • Principio de Segregación de Interfaces (ISP).
    • Principio de Inversión de Dependencias (DIP).
  2. Patrones de Diseño:
    • Patrón método de fábrica.
    • Patrón fábrica abstracta.
    • Patrón estrategia.
    • Patrón método plantilla.

Veamos una implementación del patrón estrategia:

from abc import ABC, abstractmethod

class EstrategiaOrdenamiento(ABC):
    @abstractmethod
    def ordenar(self, datos):
        pass

class OrdenamientoBurbuja(EstrategiaOrdenamiento):
    def ordenar(self, datos):
        print("Realizando ordenamiento de burbuja")
        return sorted(datos)  # Usamos sorted() de Python por simplicidad

class OrdenamientoRapido(EstrategiaOrdenamiento):
    def ordenar(self, datos):
        print("Realizando ordenamiento rápido")
        return sorted(datos)  # Usamos sorted() de Python por simplicidad

class Ordenador:
    def __init__(self, estrategia):
        self.estrategia = estrategia

    def ordenar(self, datos):
        return self.estrategia.ordenar(datos)

# Uso
datos = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

ordenador_burbuja = Ordenador(OrdenamientoBurbuja())
print(ordenador_burbuja.ordenar(datos))

ordenador_rapido = Ordenador(OrdenamientoRapido())
print(ordenador_rapido.ordenar(datos))

# Salida:
# Realizando ordenamiento de burbuja
# [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
# Realizando ordenamiento rápido
# [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

Este ejemplo del Patrón Estrategia muestra cómo la abstracción nos permite definir una familia de algoritmos, encapsular cada uno y hacerlos intercambiables. La clase Ordenador no necesita conocer los detalles de cómo funciona cada algoritmo de ordenamiento; solo sabe que puede llamar al método ordenar en cualquier objeto EstrategiaOrdenamiento.


¡Gracias por llegar hasta acá! Espero que este recorrido por el universo de la programación haya sido tan apasionante para vos como lo fue para mí escribirlo.

Nos encantaría escuchar lo que pensás, así que no te quedes callado/a, dejá tus comentarios, sugerencias y todas esas ideas copadas que seguro se te ocurrieron.

Y para ir más allá de estas líneas, date una vuelta por los ejemplos prácticos que preparamos para vos. Vas a encontrar todo el código y los proyectos en nuestro repositorio de GitHub learn-software-engineering/examples.

¡Gracias por ser parte de esta comunidad de aprendizaje. Seguí programando y explorando nuevos territorios en este fascinante mundo de la computación!