En la primera entrega de este artículo veíamos una configuración básica para persistir cómodamente nuestras entidades usando DBFlow.
En él guardabamos y recuperabamos una entidad genérica «Estudiante» (Student).
Observemos que no hemos usado el nombre Student para nuestra clase de persistencia, sino StudentDBEntry. Este es un aspecto importante que cabe destacar.
El problema de los modelos acoplados.
Imaginemos que tenemos una clase Student que usamos en todo el ámbito de nuestra App, con sus atributos, getters, y setters.
(NOTA: estas clases se denominan Plain Old Java Objects o POJOs)
Imaginemos que decidieramos persistirla directamente extendiendola de BaseModel y anotando sus atributos.
Quedaría algo parecido a lo que sigue:
Acabaríamos de crear un problema insalvable a nuestro código: Crear un acoplamiento entre nuestras clases de dominio (las entidades como el Estudiante o la Asignatura) y una librería concreta de manejo de bases de datos.
Si el dia de mañana DBFlow deja de servirnos, o encontramos otra librería que se adapta mejor a nuestras necesidades, nuestro código estará fuertemente acoplado con la misma (tight coupling), y no podremos prescindir de ella sin lamentar nuestro grave error de diseño. La probabilidad de que en esta migración aparezcan bugs es del 100%.
Es por eso que a la hora de persistir, tomaremos como hábito crear entidades aparte, y que sean estas las que están fuertemente acopladas con librerías concretas (DBFlow, SugarORM, OrmLite, Realm…).
Asímismo crearemos abstracciones que nos permitan convertir en los dos sentidos:
de la capa de persistencia (DBFlow) a la capa de dominio (entidades), y viceversa. Estas entidades se denominan Mappers o con el término inexistente en castellano «Mapeadores».
Es por eso que en la primera entrega nombrábamos nuestra clase de persistencia como StudentDBEntry y no Student.
Concluida esta aclaración, pasamos al mapper. La forma más elegante de hacerlo es declararlo abstracto y luego hacer una subclase que implemente el comportamiento.
Una aproximación podría ser la que sigue:
Y la subclase implementadora de esta interfaz, podría llamarse StudentDBMapperImpl.
En el bloque de código donde se definía StudentDBEntry veíamos un métodos parseStudent() que nos devuelve un objeto Student.
Es importante que trabajemos con la clase abstracta y no con su implementación, es decir:
MAL: for(StudentDBEntry e : list) { list.add(e.parseStudent()); } BIEN: for(StudentDBEntry e : list) { StudentDBMapper mapper = new StudentDBMapperImpl(); list.add(mapper.map(e)); }
En este ultimo trozo de código estamos aplicando el Principio de inversión de dependencias.
Es ampliamente sabido que aplicar este principio jugará siempre en nuestro favor. Aunque este caso sea sencillo y solo estemos envolviendo a la propia clase con una abstracción, aplicar inversión de dependencias nunca será una mala idea.
Y tras estas dos entregas del post, tenemos persistida nuestra entidad Estudiante, y trasladada de una capa a otra de nuestra aplicación de manera elegante y desacoplada.
Si tomamos como costumbre trabajar de este modo, tendremos multiples ventajas:
– resultará facil migrar nuestras aplicaciones de una librería (por ej. DBFlow) a otra distinta
– tendremos una capa de dominio independiente de detalles de implementación, y de librerías concretas, reduciendo al mínimo el acoplamiento entre capas.
– En definitiva, nuestro código será mucho más mantenible a largo plazo, con un sobrecoste pequeño sobre hacerlo mal (con tight coupling).
En la siguiente entrega de este artículo, trataremos el concepto de Migración, para cuando necesitemos modificar nuestra base de datos en posteriores actualizaciones de nuestra app.
0 comentarios