La herencia mal planteada

En cuestiones de ORM (Object Relational Mapping) casi siempre asociamos una relación uno a uno (1:1) en bases de datos relacionales, con una herencia de clases. Esquematizar este tipo de diseño puede llevarnos a futuros errores cuando nos planteamos la arquitectura de cualquier proyecto.

Vamos a ver ejemplos que nos permitan entender dónde está la trampa.

Si tratamos de representar la estructura de mascotas en un diagrama de clases, pudiéramos pensar que todas tienen un dueño, al igual que todas tienen un nombre y una fecha de nacimiento. De ahí, y derivando en acciones más específicas, vemos que algunas mascotas se pueden acariciar (perro, gato), y otras no tanto (por ejemplo una serpiente, sí sí, una serpiente, que dueños locos se sobran en este mundo).

Representando esto, tendríamos una clase mascota, que tendría una propiedad persona (dueño), otra propiedad fecha de nacimiento, y todas aquellas que entendamos pueden ser comunes a todas las mascotas. Luego, cada mascota, heredaría su funcionalidad e implementaría sus funciones más específicas (no quiero complicar el diagrama porque al final no es el objetivo de este artículo)

Este diagrama, traducido a una base de datos relacional, nos quedaría con tres tablas, mascota, perro y serpiente, donde tendríamos una relación de uno a uno entre ellas, y cualquier ORM decente, sabría que leer un perro implica leer la información asociada al mismo en la tabla mascota, y lo mismo ocurriría si tratamos de insertar o actualizar cualquiera de las clases que implementen mascota.

Pero…  (Siempre hay un pero)

Si analizamos esto mismo con alumnos y maestros, ¿qué pasaría? Vamos allá…

Un maestro, al igual que un alumno, son personas, ambos tienen un nombre, apellidos, padres, edad, etc. Mientras más elementos en común tengan varias clase, más confirmamos la presencia de una herencia, así que si llevamos esto a un diagrama de clases, nos quedaría de la siguiente forma:

Visto desde el punto de vista de bases de datos, igual caeríamos en tres tablas, con una relación uno a uno entre ellas (persona, maestro y alumno) y todo perfecto con el ORM. Todo perfecto hasta que a un maestro se le ocurre estudiar. Plantearnos esta situación nos crea un problema, ¿cómo digo yo que un maestro también es un alumno? ¿Cómo puedo asumir que una misma persona ahora es estudiante y maestro?

La encapsulación de la propia herencia no lo permite, yo no podría reutilizar la información de persona ahora en alumno, por lo que aquí la herencia no aplica y, la solución pasa por convertir la herencia, en una asociación de Persona tanto en la clase Maestro como en la clase Alumno, o como una alternativa, pudiéramos definir una lista de responsabilidades asociadas a una persona, de manera que pudiera ocupar la función de alumno y maestro al mismo tiempo.

¿Cómo podemos identificar este tipo de problema cuando nos enfrentamos a un diseño de cualquier aplicación?

Roles que definen responsabilidad

Los roles son actividades que pueden ser compartidas por un elemento de nuestro diseño, siempre que exista un mismo objeto que pueda compartir más de un rol, la herencia no es la solución. Es por esto que en el análisis de mascotas esto no ocurre, ya que un perro no podrá ser nunca una serpiente y tampoco viceversa, al menos hasta hoy 😉

Fabio Maulo, team leader de para mí el mejor ORM que existe hoy en día, NHibernate, hace mucho tiempo hacía una historia sobre un personaje que vive en las colinas de Italia y que ejemplificaba este problema con la herencia.

Salu2