Anemic Domain Model Antipattern
Anemic ¿Qué!??
Hoy toca hablar de patrones, o mejor dicho, de anti patrones. Esas cosas que se deben evitar por norma general.
Mucha de la gente que venimos de lenguajes procedurales (vaya palabro…) tipo C, tendemos a pensar de esta forma a la hora de programar en Java. ¿Por qué? Por que C no es un lenguaje orientado a objetos. Para representar en C un dato usamos lo siguiente:
typedef struct point { int x,y; } point;
....
y usamos punto.x
Así que cuando vamos a un lenguaje orientado a objetos tendemos a hacer cosas de este tipo:
public class Casa{
private String nombre;
private boolean alquilada;
private boolean habitable;
public void setNombre()...
public String getNombre()...
public void setAlquilada()...
public boolean isAlquilada()...
....
}
public class Persona{
private String nombre;
private List casas;
public void setNombre()...
public String getNombre()...
public void setCasas()...
public List getCasas()...
}
..
List casasNoAlquiladas= new ArrayList();
for(Persona persona: personas){
for (Casa casa : persona.getCasas()){
if (! casa.isAlquilada() && casa.isHabitable()){
casasNoAlquiladas.add(casa);
}
}
}
….
¿Cuál es el problema con esto? Pues que estamos utilizando las clases de negocio como simples estructuras de datos. Hemos avanzado en la tecnología, ¡ Avancemos en su uso también! Vale, siempre lo has hecho así, te sientes cómodo con esta solución ¿Con qué problemas te vas a encontrar?
- Lógica de negocio dispersa por toda la aplicación, seguramente incluso repetida.
- Complejidad a la hora de hacer las clases controladoras
- Estamos exponiendo la forma en que mantenemos las casas al exterior. Solo debemos exponer las entities, no los value objects! (esto daría para otro post)
- ¿La lista de casas que devolvemos es inmutable? ¡Cuidado!
- Sumando todo lo anterior: Más complejidad = más bugs, además de menos extensible
Bueno y ¿Cómo lo solucionaríamos? Muy fácil, introduciendo la lógica de negocio en las clases de negocio:
public class Casa{
….
private final String nombre;
private boolean alquilada;
private boolean habitable;
public String getNombre()...
public void setAlquilada()...
public boolean isAlquilada()...
public boolean isAlquilable(){
return (! casa.isAlquilada() && casa.isHabitable());
}
….
}
public class Persona{
private String nombre;
private List casas;
public void setNombre()...
public String getNombre()...
public void setCasas()...
public List getCasasAlquilables(){
List list= new ArrayList();
for (Casa casa: casas){
if (casa.isAlquilable()) list.add(casa);
}
return list;
}
}
..
List casasNoAlquiladas= new ArrayList();
for(Persona persona: personas){
casasNoAlquiladas.addAll(persona.getCasasAlquilables());
}
Más fácil ¿Verdad? Si no conocías este error común, seguro que ahora te vienen a la memoria un montón de casos en los que podrías ahorrar mucho en código y hacerlo mucho más extensible.
Otro ejemplo: En cierto cliente, hace un cierto tiempo y haciendo cierta aplicación
De una clase muy importante en el modelo (Entity) sacabamos unos cuantos informes. En un momento dado, me pidieron que si se podía cambiar cierta lógica a la hora de generar los informes. Mi respuesta fue: tardo 2 segundos (y uno para pulsar ctrl+shif+T en Eclipse!! ). ¿Por qué? Porque mi clase de ese entity tan importante tenía lo siguiente:
List<Dato> getSubObjetosQueCumplenCiertaCondiciónDeNegocio(){
for....{
if (obj.condicion1() && obj.condicion2() && entity.....)
lista.add(...)
…
}
}
Lo que me pedían era un cambio de negocio. Yo había entendido mál, un objeto de tipo Dato debía de cumplir obj.condicion() && obj.condicion3(), pero el cambio no suponía el menor esfuerzo. ¿Cuánto hubiese costado si hubiesemos tenido este código disperso por toda la aplicación?
Se puede discutir si incluir los DAO dentro del objeto de negocio es bueno o malo (Rails y Grails lo hacen), si algúna lógica concreta debería ir en los controladores, pero al menos este tipo de cosas creo que pueden ayudarte bastante.
Bueno, no se si ha quedado claro, hoy no he dormido mucho y estoy muuuuuy cansado ![]()