Modelos anémicos vs enriquecidos

En la programación orientada a objetos, la manera en que diseñamos nuestras clases puede tener un impacto significativo en la calidad y mantenibilidad de nuestro código. Dos enfoques comunes para diseñar clases son las clases anémicas y las clases enriquecidas. Estos dos enfoques representan diferentes filosofías sobre cómo estructurar la lógica y los datos dentro de nuestras aplicaciones.

En Java en particular es común el uso de modelos (entitys), objetos de transferencia de datos (dto) y objetos planos de Java (pojos), al crear este tipo de elementos podemos hacerlo usando simples clases con atributos, getters y setters (clases con anemia) o incluso podemos agregar algo de lógica (enriquecidas). En este artículo hablaremos sobre esto.

Clases anémicas

  • En el patrón de diseño anémico, las clases son principalmente contenedores de datos sin lógica significativa.
  • Típicamente, estas clases tienen solo atributos para almacenar datos y métodos de acceso (getters y setters) para manipular esos datos.
  • La lógica de negocio se encuentra generalmente en otro lugar, como en clases de servicios o controladores.
  • Este enfoque puede violar los principios de encapsulamiento y cohesión, ya que la lógica relacionada con los datos no está contenida dentro de la clase misma.

Veamos un ejemplo de clase anémica.

public class BankAccount {
    private String accountNumber;
    private double balance;

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}

En este ejemplo no hay mucho para agregar, es solo una clase con atributos, setters y getters. Obviamente con lombok esto quedaría más simple, pero la idea es reflejar que no hay lógica adicional.

Clases enriquecidas

  • Las clases enriquecidas encapsulan tanto los datos como las operaciones relacionadas con esos datos.
  • Además de tener campos para almacenar datos, estas clases también contienen métodos que operan en esos datos.
  • La idea es que una clase no solo debería tener datos, sino también comportamiento asociado que manipule esos datos.
  • Este enfoque promueve una mejor encapsulación y cohesión, ya que la lógica relacionada con los datos está contenida dentro de la misma clase.

Veamos una clase enriquecida.

public class BankAccount {
    private String accountNumber;
    private double balance;

    public BankAccount(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    public void deposit(double amount) {
        if (amount > 0) {
            this.balance += amount;
        } else {
            throw new IllegalArgumentException("Deposit amount must be greater than zero.");
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && this.balance >= amount) {
            this.balance -= amount;
        } else {
            throw new IllegalArgumentException("Insufficient funds or invalid amount.");
        }
    }

    public void transfer(BankAccount destinationAccount, double amount) {
        if (amount > 0 && this.balance >= amount) {
            this.withdraw(amount);
            destinationAccount.deposit(amount);
        } else {
            throw new IllegalArgumentException("Insufficient funds or invalid amount.");
        }
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public double getBalance() {
        return balance;
    }
}

Existe una gran diferencia, esta clase es capaz de manejar lógica propia respecto a sus atributos, ofrece un mejor encapsulamiento y cohesion, dos características deseados en el desarrollo de software de calidad.

Antes de terminar este artículo me gustaría referirme al tema de la inmutabilidad. Clases inmutables no permiten agregar lógica enriquecida que cambie el valor sus atributos, propiamente porque son inmutables. Existen escenarios donde podemos y queremos este tipo de comportamiento de inmutabilidad, entonces no siempre es necesario que usemos el enfoque de clases enriquecidas, como siempre usemos cada enfoque en dependencia de los casos de uso.

Si te ha gustado este artículo compártelo y suscríbete a nuestra newsletter.