Ejemplo de MVC y diseño por capas: el Modelo


En la entrada anterior diseñamos las capas de nuestra aplicación siguiendo el patrón MVC. En esta nueva entrada veremos un ejemplo de implementación de la capa inferior, es decir, el Modelo.

Recordemos que hasta el momento definimos una clase de dominio Fabricante y una interfaz IFabricantesModel que define el comportamiento esperado en esta capa, de modo tal que los detalles de implementación no fueran relevantes para nuestro diseño. Ahora bien, llegado el momento de implementar la capa, es necesario conocer el contexto en el cual la misma deberá funcionar:

  • Los datos de la aplicación serán persistidos en una base de datos MySQL.
  • Utilizaremos JPA (Java Persistence API) para el mapeo objeto-relacional. Por lo tanto nuestra clase Fabricante no será un simple POJO sino una entidad JPA.
  • No utilizaremos ningún contenedor para gestionar las unidades de persistencia, razón por la cual deberemos configurar la/s unidad/es de persistencia necesaria/s a través del archivo persistence.xml
  • Asimismo, tampoco utilizaremos JTA (Java Transaction API) para el manejo de transacciones, por lo que deberemos gestionarlas por nuestra cuenta.

Bien, comencemos entonces con la implementación.

Creando la base de datos, tablas y usuarios

Como primer paso, crearemos nuestra base de datos, una tabla Fabricantes para persistir esta entidad y un usuario con los permisos necesarios para conectarse a la base de datos y operar con dicha tabla.

Nota: en líneas generales, es una buena práctica en materia de seguridad el crear uno o más usuarios dedicados para nuestra aplicación, con el mínimo de permisos necesarios para operar. De este modo podemos reducir el impacto de ataques de Inyección SQL desde la misma conexión a la base de datos. Un tema muy interesante que merece su propio post y escapa a los propósitos de este ejemplo.

DROP DATABASE IF EXISTS EjemploMVC;
CREATE DATABASE EjemploMVC;
USE EjemploMVC;

DROP TABLE IF EXISTS Fabricantes;

CREATE TABLE Fabricantes (
    idfabricante BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    nombre VARCHAR(250),
    fechafundacion DATE,
    PRIMARY KEY (idfabricante),
    UNIQUE KEY uk_fabricantes_nombre (nombre)
);

CREATE USER 'mvc'@'localhost' IDENTIFIED BY 'JavaDeepCafe';
GRANT USAGE ON EjemploMVC.* TO 'mvc'@'localhost';
GRANT SELECT, INSERT, UPDATE, DELETE ON EjemploMVC.Fabricantes TO 'mvc'@'localhost';

Configurando JPA: persistence.xml

El siguiente paso es configurar JPA en nuestra aplicación, es decir, definir nuestro archivo persistence.xml. Como mencionaba anteriormente, nuestra aplicación no se conectará a ningún contenedor para obtener un EntityManager a través de inyección de recursos, ni  haremos uso de JTA para el manejo de transacciones. Por lo tanto deberemos definir la unidad de persistencia y manejar las transacciones y el ciclo de vida del/los entity managers por nuestra cuenta.

Llamaremos a nuestra unidad de persistencia «MvcDemoPU» e indicaremos que el tipo de transacción es «RESOURCE_LOCAL»

Nota: el siguiente archivo persistence.xml está configurado para trabajar con la implementación EclipseLink JPA 2.1. Sin embargo, si se desea utilizar otro proveedor como Hibernate JPA, los cambios necesarios son mínimos.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" 
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  
  <persistence-unit name="MvcDemoPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider<!--provider>
    <class>example.mvc.model.Fabricante<!--class>
    <properties>
      <!-- Propiedades de conexión a la base de datos -->
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/EjemploMVC"/>
      <property name="javax.persistence.jdbc.password" value="JavaDeepCafe"/>
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.user" value="mvc"/>
      <!-- Propiedades de EclipseLink para habilitar el Log de las operaciones -->
      <!-- Es muy útil para ver por consola las sentencias SQL ejecutadas -->
      <property name="eclipselink.logging.logger" value="DefaultLogger"/>
      <property name="eclipselink.logging.level" value="FINE"/>
      <property name="eclipselink.logging.level.sql" value="FINE"/>
      <property name="eclipselink.logging.parameters" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

Nuestro modelo en Java: package example.mvc.model

Ha llegado el momento de escribir nuestro modelo en Java, para lo cual definiremos un package llamado example.mvc.model y que contendrá nuestras clases e interfaces correspondientes al Modelo. Es necesario destacar que la implementación de una capa no sólo tendrá las clases e interfaces que hemos diseñado, sino que también puede contener clases y artefactos adicionales para ayudar a cumplir con el contrato especificado en el diseño. En este caso incluiremos la implementación de la interfaz IFabricantesModel y una clase que se encargará de gestionar el ciclo de vida del EntityManager que se encarga de gestionar la persistencia. Tenemos entonces nuestro package:

Package example.mvc.model

Fabricante

Comenzaremos con nuestra clase de datos, la cual como dijimos es una entidad JPA, y como tal, además de las anotaciones correspondientes, debe implementar la interfaz Serializable, para lo cual sobrescribimos los métodos equals() y haschCode() heredados de la clase Object. También resulta oportuno sobrescribir el método toString().

package example.mvc.model;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

/**
 * @author Delcio Amarillo
 */
@Entity
@Table(name = "Fabricantes")
public class Fabricante implements Serializable {
    
    private static final long serialVersionUID = 6787411012408357010L;
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private BigInteger idFabricante;
    
    @Basic
    private String nombre;
    
    @Temporal(TemporalType.DATE)
    private Date fechaFundacion;
    
    /**
     * Crea una nueva instancia de la clase {@code Fabricante}.
     * @deprecated Obligatorio para JPA, NO debe utilizarse explícitamente.
     * @see #Fabricante(java.lang.String, java.util.Date) 
     */
    public Fabricante() {
        super();
    }

    public Fabricante(String nombre, Date fechaFundacion) {
        this.nombre = nombre;
        this.fechaFundacion = fechaFundacion;
    }

    public BigInteger getId() {
        return idFabricante;
    }
    
    public void setId(BigInteger id) {
        // Esta propiedad no debe ser modificada explícitamente!
        throw new UnsupportedOperationException("Not supported!");
    }

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public Date getFechaFundacion() {
        return fechaFundacion != null ? new Date(fechaFundacion.getTime()) : null;
    }

    public void setFechaFundacion(Date fechaFundacion) {
        this.fechaFundacion = fechaFundacion != null 
                            ? new Date(fechaFundacion.getTime()) : null;
    }
    
    @Override
    public int hashCode() {
        return idFabricante != null ? idFabricante.hashCode() : 0;
    }

    @Override
    public boolean equals(Object object) {
        if (object instanceof Fabricante) {
            Fabricante other = (Fabricante) object;
            return (this.idFabricante != null || other.idFabricante == null) 
                && (this.idFabricante == null || this.idFabricante.equals(other.idFabricante));
        }
        return false;
    }

    @Override
    public String toString() {
        return "example.mvc.model.Fabricante[ id=" + idFabricante + " ]";
    }
}

Observaciones:

  • Nótese que el método setId() lanza una UnsupportedOperationException y esta decisión obcedece al hecho de que en general no es necesario (ni posible, según las reglas de negocio) actualizar un identificador único. Como JPA trabaja con la API Reflection, no necesita ningún setter para modificar el valor de los atributos de una entidad. Por lo tanto, mientras más restringido tengamos el estado interno de nuestra clase, mejor (principio de encapsulamiento).
  • Al trabajar con objetos Date en Java, debemos tomar recaudos extra porque dicha clase NO es inmutable. Por lo tanto, si devolvemos directamente el atributo fechaFundacion en lugar de una copia, podría modificarse su estado interno desde el exterior y provocar efectos no deseados en nuestra clase Fabricante. Del mismo modo, en el setter también asignamos una copia del parámetro para evitar que las referencias externas, si son modificadas, puedan afectar el estado interno de nuestra clase.

EntityManagerProvider

Como indica el estereotipo en el diagrama, esta clase implementará el patrón Singleton con la finalidad de mantener una única instancia de la clase a lo largo de todo el ciclo de vida de la ejecución de nuestra aplicación.

package example.mvc.model;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 * @author Delcio Amarillo
 */
public class EntityManagerProvider {
    
    private EntityManagerFactory entityManagerFactory;
    private EntityManager entityManager;
    private static EntityManagerProvider provider;
    
    
    private EntityManagerProvider() {
        initEntityManager();
    }

    private void initEntityManager() {
        entityManagerFactory = Persistence.createEntityManagerFactory("MvcDemoPU");
        entityManager = entityManagerFactory.createEntityManager();
    }
    
    public static synchronized EntityManagerProvider getProvider() {
        if (provider == null) {
            provider = new EntityManagerProvider();
        }
        return provider;
    }
    
    public EntityManager getEntityManager() {
        return entityManager;
    }
    
    public void closeResources() {
        if (entityManager != null && entityManager.isOpen()) {
            entityManager.close();
        }
        
        if (entityManagerFactory != null && entityManagerFactory.isOpen()) {
            entityManagerFactory.close();
        }
    }
}

Es necesario mencionar que esta implementación del patrón Singleton funciona si el ciclo de vida de ejecución tiene lugar en la misma JVM, como es el caso de una aplicación de escritorio. En el caso de aplicaciones Web, el encargado de iniciar una nueva JVM es el servidor o contenedor Web y la misma es única para múltiples Servlets. Por lo tanto en ese entorno es necesario implementar el patrón ThreadLocal o bien utilizar Session Beans anotados con @Singleton.

IFabricantesModel

He aquí el código de nuestra interfaz de modelo:

package example.mvc.model;

import java.math.BigInteger;
import java.util.List;

/**
 * @author Delcio Amarillo
 */
public interface IFabricantesModel {
    
    public static final int EXITO = 0;
    
    public static final int FALLO = -1;
    
    public Fabricante getFabricantePorId(BigInteger id);
    
    public List<Fabricante> getFabricantes();
    
    public int insertar(Fabricante fabricante);
    
    public int modificar(Fabricante fabricante);
    
    public int eliminar(Fabricante fabricante);
    
    public int eliminar(List<Fabricante> fabricantes);    
}

Nótese que se agregaron dos constantes que podrán ser utilizadas por la capa superior para evaluar el resultado de la operación en esta capa. Como vimos en la entrada anterior, el Modelo se comunicará con el Controlador a través del resultado de sus operaciones, no por notificación directa.

FabricantesModelJpa

Finalmente, llegó la hora de ver el código de la clase concreta que implementa nuestra interfaz IFabricantesModel.

package example.mvc.model;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.TypedQuery;

/**
 * @author Delcio Amarillo
 */
public class FabricantesModelJpa implements IFabricantesModel {
    
    private final EntityManager entityManager;
    
    public FabricantesModelJpa(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public Fabricante getFabricantePorId(BigInteger id) {
        return entityManager.find(Fabricante.class, id);
    }

    @Override
    public List<Fabricante> getFabricantes() {
        List<Fabricante> fabricantes = new ArrayList<>();
        String jpql = "SELECT f FROM Fabricante f";
        TypedQuery<Fabricante> query = entityManager.createQuery(jpql, Fabricante.class);
        fabricantes.addAll(query.getResultList());
        return fabricantes;
    }

    @Override
    public int insertar(Fabricante fabricante) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            entityManager.persist(fabricante);
            transaction.commit();
            return EXITO;
        } catch (EntityExistsException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
            transaction.rollback();
            return FALLO;
        }
    }

    @Override
    public int modificar(Fabricante fabricante) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            entityManager.merge(fabricante);
            transaction.commit();
            return EXITO;
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
            transaction.rollback();
            return FALLO;
        }
    }

    @Override
    public int eliminar(Fabricante fabricante) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            Fabricante fabricanteAEliminar = entityManager.getReference(Fabricante.class, fabricante.getId());
            entityManager.remove(fabricanteAEliminar);
            transaction.commit();
            return EXITO;
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
            transaction.rollback();
            return FALLO;
        }
    }

    @Override
    public int eliminar(List<Fabricante> fabricantes) {
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        try {
            for (Fabricante fabricante : fabricantes) {
                Fabricante fabricanteAEliminar = entityManager.getReference(Fabricante.class, fabricante.getId());
                entityManager.remove(fabricanteAEliminar);
            }
            transaction.commit();
            return EXITO;
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
            transaction.rollback();
            return FALLO;
        }
    }
}

 Resulta útil remarcar en este punto la ventaja de haber definido nuestra interfaz de modelo siguiendo el patrón DAO: la clase EntityManager tiene su correlato casi directo para cada uno de nuestros métodos, con lo cual resulta muy sencillo implementar esta capa con JPA.

Nótese también que esta implementación toma como parámetro un EntityManager al instanciar la clase, para lo cual es conveniente la clase EntityManagerProvider vista anteriormente. Por ejemplo:

EntityManager entityManager = EntityManagerProvider.getProvider().getEntityManager();
IFabricantesModel model = new FabricantesModelJpa(entityManager);

Conclusión

En esta entrada vimos un ejemplo de implementación de nuestra capa de Modelo basada en algunos requerimientos no funcionales. Si en lugar de JPA se deseara utilizar JDBC plano, entonces sólo deberíamos quitar las anotaciones de nuestra clase Fabricante y crear una nueva implementación de la interfaz IFabricantesModel que haga uso de dicha API.

En la siguiente entrada, veremos un ejemplo de implementación de la capa Controlador.

Código fuente

Pueden encontrar el código del ejemplo DemoModelViewController en GitHub El mismo tiene la estructura de un proyecto Maven, con un archivo pom.xml y una carpeta src donde se encuentra el código fuente.

12 comentarios en “Ejemplo de MVC y diseño por capas: el Modelo

  1. Hernán

    Hola Delcio, cómo andás? Te consulto porque comencé a incorporar esta parte al proyecto, se me retrasó todo por varias cuestiones, y compila bien, pero al ejecutarlo me dice: ‘org.hibernate.hql.internal.ast.QuerySyntaxException: usuarios is not mapped [select u from usuarios u]’. ‘usuarios’ es mi tabla ‘fabricantes’. Ya probé usando Maven, en NetBeans y también en IntelliJ, pero ambos me dicen lo mismo. También intenté cambiar de EclipseLink a HIbernate como proveedor de persistencia, de agregarle un archivo ‘hibernate.cfg.xml’ para mapear el bean, y nada. En la base de datos la tabla se genera, las columnas también, pero me dice eso del mapeo de la tabla que no sé de dónde sale, y no se ejecuta la ventana principal tampoco.
    Bueno, espero que estés bien, saludos!

    Me gusta

    Responder
    1. Delcio Amarillo Autor

      Hola Hernán! A simple vista, sin ver nada de tu proyecto, te diría que el error es que no hay una entidad «usuarios» para mapear y te diría que el error se origina en el JPQL (sí, el lenguaje de consulta no es SQL sino JQPL, es muy similar pero no exactamente igual). En la consulta tenés que poner el nombre de tu clase (la entidad JPA) como si fuera el nombre de la tabla: «SELECT u FROM Usuario u», donde Usuario es el nombre de tu entidad JPA.

      Fijate en el ejemplo, la entidad se llama Fabricante (en singular) mientras que la tabla se llama Fabricantes (en plural). Para indicarle al PersistenceProvider que el nombre de la tabla es diferente al nombre de la entidad, se usa la anotación @Table para especificar cómo se llama la tabla. De otro modo asume que la tabla se llama exactamente igual que la entidad.

      Cualquier cosa si te sigue dando errores, enviame tu proyecto y te doy una mano. Saludos!

      Me gusta

      Responder
  2. Hernán

    Gracias Delcio! Mirá, ahora ya me ejecuta el ‘inicio’, me muestra la ventana principal, pero me lanza otros errores que iré viendo al hacer las operaciones, recién empiezo a modificarlo. Le quité la anotación ‘@Table(name = «Usuarios»)’ y la reemplacé por el nombre del bean, ‘User’, en el ‘String jpql’, y entonces sí se ejecutó. La verdad que no sé por qué razón no me toma esa anotación, pero por lo menos ya veo que se debe a eso.
    Voy a ver si puedo solucionar lo que voy encontrando, si se me complica demasiado te consulto, pero por lo pronto ya tengo acceso a la ventana principal.

    Muchas gracias Delcio, saludos!

    Me gusta

    Responder
  3. Hernán

    Ya está! Tenía otros problemas con un atributo ‘id’ de mi ejemplo, y me generaba conflicto porque estaba asignado en el constructor del bean, lo modifiqué y se solucionó todo (me lanzaba ‘PersistentObjectException: detached entity pased to persist’, estaba tratando de persistir algo ya ‘detachado’ en el contexto de persistencia).
    Te pregunto algo: con ese singleton obtenemos un solo ‘EntityManagerFactory’ global, no? Porque tenía entendido que ese era el más costoso, y que había que generar solamente uno para todo el ciclo de vida de la aplicación, a diferencia del ‘EntityManager’ que se abre y cierra con cada consulta…
    Gracias de nuevo Delcio, saludos!

    Me gusta

    Responder
    1. Delcio Amarillo Autor

      Hola Hernán! Buenísimo que pudiste solucionar el problema. Con respecto a tu pregunta, en el ejemplo singleton se encarga que ambos EntityManagerFactory y EntityManager sean únicos a lo largo del ciclo de vida de la aplicación porque la creación de de la factory es sumamente costosa en términos de tiempo y recursos y porque el contexto de persistencia es el mismo en toda el ciclo de vida de la aplicación, razón por la cual sólo se cierra el EntityManager al final. Otro enfoque es efectivamente crear y cerrar un EntityManager por transacción, para lo cual podemos modificar el método getEntityManager() como sigue:

      public EntityManager getEntityManager() {
          if (entityManager == null || !entityManager.isOpen()) {
              entityManager = entityManagerFactory.createEntityManager();
          }
          return entityManager;
      }
      

      En líneas generales, no existe una forma correcta, ambas son válidas y dependen de cómo esté diseñada nuestra aplicación. En lo personal me gusta mucho la explicación que dan en StackOverflow (está en inglés, sorry about that). También (en inglés) hay explicaciones conceptuales aquí y aquí (estas dos últimas son de mi autoría). Espero te sirvan. Saludos!

      Me gusta

      Responder
  4. Hernán

    Excelente, Delcio, ahora me queda más claro. El curso que hice no llegó a mostrarnos el singleton, solamente comentaron su existencia y funcionalidad a grandes rasgos. Ahora arranco uno de Spring, son solamente cuatro clases, y se complementa bien con Hibernate según me dijeron. Pero son todos cursos así, cortos e introductorios, te dejan en el principio del camino, después no sabés muy bien cómo seguir si no estás trabajando en el ambiente (ni hablar cuando te toca un profesor que viene ‘del lado del análisis y no ‘del lado del código’). El mundo de Java es enorme, y el de la programación en general hace rato que resulta inabarcable. Aunque hay aspectos en común que agilizan la comprensión de temas diversos, en la especialización uno se pierde irremediablemente. Está la red, claro, pero lleva mucho tiempo adaptar ejemplos a lo que uno quiere hacer, y no todo es tan fácil de modificar ‘modularmente’, más cuando se está en etapa de formación.
    Y paso por ese foro habitualmente, tienen muy buenos datos también (creo que es el mejor sobre esta temática), y con el inglés, si bien no soy un experto, me defiendo relativamente bien, en este campo (programación) no hay muchas opciones lamentablemente.

    Gracias Delcio, saludos!

    Me gusta

    Responder
    1. Delcio Amarillo Autor

      Ciertamente, las tecnologías son muchas y complejas y los tiempos de los cursos generalemente son escasos. Un buen sitio en español para consultar es http://www.arquitecturajava.com/ y un excelente libro para conocer distintos frameworks y buenas prácticas es Arquitectura Java Sólida de Cecilio Álvarez Caules. Sin explayarse demasiado en cada framework, hay una pequeña introducción de cada uno (inclusive Spring) y ejemplos orientados a aplicar distintos principios y patrones de diseño.

      Me gusta

      Responder
  5. Hernán

    Hola Delcio, conozco el blog, alguna vez pasé por allí, es verdad que está muy bueno. Es claro y didáctico, y también se ocupa de temas clave. Su libro lo tengo en la lista de pendientes.
    Lo que voy viendo de Spring es interesante, vamos a una velocidad muy alta, solamente se presentan algunos módulos y se explica su objetivo (además de cómo usarlo con algún ejemplo), porque es una biblioteca enorme la que maneja. Algunas cosas no me agradan tanto, pero seguramente todavía no las comprenda demasiado como para apreciar las ventajas.
    Saludos!

    Me gusta

    Responder
  6. Gustavo Echenique

    Estimado Delcio:
    Muy buena tu serie dedicada al diseño por capas.
    ¿Sabés algo?, estoy trabajando con Eclipse, y si genero las entidades a partir de un origen de datos, no puedo hacer que se cumpla el «contrato» entre la interfaz y la clase. Salvo que sea picando código.
    ¿Hay alguna forma más diligente de hacerlo?
    Saludos!

    Le gusta a 1 persona

    Responder
    1. Delcio Amarillo Autor

      Hola Gustavo! Gracias por dejar tu comentario. La verdad es que no uso Eclipse, pero a grandes rasgos la generación automática de código suele dar más trabajo del que nos ahorra y esto es así independientemente del IDE. A modo de opinión personal, es preferible sacrificar la generación automática de código por un buen diseño e implementación.

      Ahora bien existen técnicas, patrones y frameworks que nos a yudan a crear código altamente reutilizable, sin librarnos de tener que hacer código a mano pero reduciendo bastante la carga. Concretamente en lo que respecta a persistencia, el framework Spring provee mecanismos para implementar el patrón DAO utilizando JDBC, JPA o Hibernate. Esto sumado al soporte para Inyección de Dependencias y manejo de transacciones, que son el core y el punto fuerte de Spring, nos permite contar con implementaciones en cuestión de minutos.

      Me gusta

      Responder
    1. Delcio Amarillo Autor

      Este comentario va de la mano del anterior y creo que esta respuesta te puede orientar un poco: https://javaparasereshumanos.wordpress.com/2014/12/29/ejemplo-de-mvc-y-diseno-por-capas-la-vista/comment-page-1/#comment-53

      Quizás se puede plantear un esquema así:
      – Una interfaz genérica para definir las operaciones básicas de cada DAO.
      – Una interfaz especializada por cada entidad en nuestro modelo.
      – Una clase abstracta que implemente las operaciones básicas del DAO.
      – Una clase concreta por cada entidad del modelo que extienda de la clase abstracta y que a su vez implemente los métodos de la interfaz específica de esa clase de dominio.

      Suena complicado pero si se hace bien nos ahorramos mucho código repetitivo. Quizás pueda hacer algo y postearlo en una nueva entrada 😉

      Me gusta

      Responder

Deja un comentario