Diseño de clases limpias desarrollando software

El diseño de clases limpias es esencial para crear software que sea fácil de mantener, entender y escalar. Una clase bien diseñada sigue principios que aseguran su cohesión y minimizan su acoplamiento con otras clases. Esto no solo facilita la comprensión del código, sino que también reduce la complejidad a la hora de realizar modificaciones o añadir nuevas funcionalidades.

En este apartado, exploraremos dos principios fundamentales para el diseño de clases limpias: la responsabilidad única y la cohesión y acoplamiento.

  1. Responsabilidad única: veremos cómo aplicar el principio de responsabilidad única (SRP) para asegurar que cada clase tenga una única razón para cambiar, mejorando así la mantenibilidad y claridad del código.
  2. Cohesión y acoplamiento: discutiremos cómo lograr una alta cohesión dentro de nuestras clases, asegurando que todos los métodos y propiedades estén estrechamente relacionados. Además, aprenderemos a minimizar el acoplamiento, evitando dependencias innecesarias entre clases para fomentar un diseño más modular y flexible.

Estos principios son fundamentales para el desarrollo de software limpio y eficiente, y su correcta aplicación puede transformar significativamente la calidad de tus proyectos.

Responsabilidad única en clases

El Principio de Responsabilidad Única (SRP, Single Responsability Point) es uno de los fundamentos del desarrollo de software limpio.

Este principio establece que una clase debe tener solo una razón para cambiar, es decir, debe tener una única responsabilidad. Al adherirse a este principio, se mejora la mantenibilidad y la claridad del código, al tiempo que se facilita el proceso de pruebas y la identificación y corrección de errores.

Por ejemplo, considera una clase "Empleado" que realiza múltiples funciones:


public class Empleado {
    private String nombre;
    private String puesto;
    private double salario;

    public void calcularSalario() {
        // lógica para calcular el salario
    }

    public void guardarEnBaseDeDatos() {
        // lógica para guardar los datos en la base de datos
    }

    public void generarReporte() {
        // lógica para generar un reporte
    }
}

En este ejemplo, la clase "Empleado" está asumiendo múltiples responsabilidades: cálculo de salario, almacenamiento en base de datos y generación de reportes. Para adherirse al principio SRP, estas responsabilidades deben ser repartidas en clases separadas.

Podemos refactorizar el código de la siguiente manera:


public class Empleado {
    private String nombre;
    private String puesto;
    private double salario;

    // Constructores, getters y setters
}

public class CalculadorSalario {
    public void calcular(Empleado empleado) {
        // lógica para calcular el salario
    }
}

public class AlmacenadorEmpleado {
    public void guardar(Empleado empleado) {
        // lógica para guardar los datos en la base de datos
    }
}

public class GeneradorReporteEmpleado {
    public void generar(Empleado empleado) {
        // lógica para generar un reporte
    }
}

De esta manera, se delegan las responsabilidades a clases específicas y la clase "Empleado" asume solo la responsabilidad de manejar los datos del empleado.

Cohesión y acoplamiento en clases

La cohesión y el acoplamiento son dos conceptos cruciales para el diseño de clases limpias.

La cohesión se refiere a qué tan estrechamente están relacionadas y enfocadas están las responsabilidades de una clase, mientras que el acoplamiento se refiere a cómo una clase depende de otras clases para funcionar.

Una clase con alta cohesión contiene métodos y propiedades que se relacionan estrechamente entre sí y contribuyen a un propósito definido. Por otro lado, una clase con bajo acoplamiento minimiza sus dependencias en otras clases, permitiendo un diseño más modular y flexible.

Analicemos el siguiente ejemplo para ilustrar estos conceptos:

public class Factura {
    private List productos;
    private AlmacenadorFactura almacenadorFactura;

    public double calcularTotal() {
        double total = 0;
        for (Producto producto : productos) {
            total += producto.getPrecio();
        }
        return total;
    }

    public void guardarFactura() {
        almacenadorFactura.guardar(this);
    }
}

En este caso, la clase "Factura" tiene baja cohesión y alto acoplamiento. Está realizando múltiples responsabilidades (calcular el total y guardar la factura) y depende de la clase "AlmacenadorFactura" para funcionar correctamente.

Podemos mejorar la cohesión y desacoplar la clase "Factura" de la siguiente forma:

public class Factura {
    private List productos;

    public double calcularTotal() {
        double total = 0;
        for (Producto producto : productos) {
            total += producto.getPrecio();
        }
        return total;
    }
}

public class AlmacenadorFactura {
    public void guardar(Factura factura) {
        // lógica para guardar la factura
    }
}

Ahora, la clase "Factura" tiene alta cohesión y bajo acoplamiento al centrarse únicamente en el cálculo del total. La responsabilidad de guardar la factura se ha trasladado a la clase "AlmacenadorFactura", logrando un diseño más limpio y mantenible.