¿Debería un DAO ser singleton o no?

14

Estoy desarrollando una API RESTful y creo que es conveniente usar DAO para mis recursos porque aunque planeo usar solo memoria para almacenarlos, no quiero cerrar la puerta a quien esté usando mi biblioteca si deciden usar Una implementación de base de datos para el DAO.

Mi pregunta es si el DAO debería ser un singleton o no. Si no es así, el servicio tendrá una instancia del DAO y se vería más o menos así:

@Path("eventscheduler")
public class EventSchedulerService {
    private IEventSchedulerDao dao = new EventSchedulerDao();

    // in case a different implementation is to be used
    public void setEventSchedulerDao(IEventSchedulerDao dao) {
        this.dao = dao;
    }

    @Path("{uniqueName}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Tournament getTournament(@PathParam("name") String uniqueName) {
        return dao.get(uniqueName);
    }

    @Path("create")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Tournament createTournament(Tournament tournament) {
        return dao.create(tournament);
    }
}

Si bien el DAO era un singleton, pero supongo que no habría mucha diferencia, solo en la primera línea:

private IEventSchedulerDao dao = EventSchedulerDao.getInstance();

Todavía tendría que usar una IEventSchedulerDaoinstancia, pero supongo que todos los singletons funcionan así, ¿verdad? Por alguna razón, siempre correlaciono los singletons con los métodos estáticos, por lo que, en lugar de tener una instancia de singleton visible para el usuario getInstance(), esto estaría oculto y él / ella simplemente usaría EventSchedulerDao.get(name), etc ... de manera estática. ¿Es esto una cosa o solo soy yo?

Entonces, ¿debería o no debería tener DAO singleton?

Y como pregunta adicional, ¿está bien que mi enfoque tenga puertas abiertas para que el usuario implemente sus propios DAO?

dabadaba
fuente
Puede usar un singleton de IoC en lugar de un singleton con un descriptor de acceso estático.
CodesInChaos

Respuestas:

10

No usaría un singleton. Es un antipatrón reconocido y dificulta las pruebas. Preferiría inyectar en una implementación concreta, y hacer que su servicio haga referencia a una interfaz DAO (que le permite inyectar diferentes implementaciones)

Brian Agnew
fuente
1
Lo que sugieres en tu última oración es precisamente lo que estoy haciendo, ¿verdad?
dabadaba
1
Estás haciendo referencia a través de una interfaz (sí), pero no estás inyectando el DAO (para ser claro ...)
Brian Agnew
¿Qué quieres decir? Tengo un setter para eso, ¿no?
dabadaba
@dabadaba La línea private IEventSchedulerDao dao = new EventSchedulerDao();es donde te has equivocado. La implementación de IEventSchedulerDaodebe inyectarse a través del constructor y nunca cambiarse (es decir, deshacerse de él setEventSchedulerDaotambién).
David Arno
De acuerdo, lo entiendo. Acabo de hacer eso para proporcionar un DAO predeterminado y cambiarlo sería "opcional". Pero tomar su sugerencia significa tener un constructor para el servicio diferente al predeterminado, y sinceramente, no tengo idea de cómo funciona eso con Jersey porque usa el constructor predeterminado. ¿Por casualidad sabes cómo hacer eso?
dabadaba
4

Un D ata A cceso O bject sólo debe existir realmente una vez en su aplicación. La lógica sigue siendo la misma, lo único que es diferente son los valores que entran y salen de los métodos que proporciona el DAO.

Con eso en mente, obviamente lo primero que suele suceder es implementar el DAO como un singleton fuerte , es decir, cuando tienes un staticmétodo en una clase de fábrica, algo así como getInstancecargar de forma diferida una instancia del DAO si es nulo y devolverlo.

Disculpe si la sintaxis no es completamente correcta, no soy un programador de Java.

class DaoSingletonFactory
{
    private static Dao dao = null;

    public static Dao getInstance()
    {
        if (DaoSingletonFactory.dao == null) {
            DaoSingletonFactory.dao = new Dao();
        }

        return DaoSingletonFactory.dao;
    }
}

class UsesDao
{
    public void someMethod()
    {
        Dao dao = DaoSingletonFactory.getInstance();
    }
}

Esto es increíblemente difícil de probar, porque no se puede cambiar la implementación sin alterar el código de la UsesDaoclase. Eso se puede hacer a través de algunos parches de mono , pero generalmente no se considera una buena práctica.

Luego está la mejor manera, el patrón de singleton débil , donde no recuperas una instancia a través de un staticmétodo, sino que haces que todas las clases dependan de la instancia, ya sea a través de un constructor o un setter (en tu EventSchedulerServiceestás usando la inyección setter).

El único problema es que debe asegurarse de que todas las clases, que dependen de una instancia de clase que solo debería existir una vez que su ciclo de vida de la aplicación, estén tomando la misma instancia que su parámetro, es decir. el newse llama una sola vez en el objeto DAO en toda la aplicación.

Obviamente, esto es increíblemente difícil de rastrear y construir el gráfico de objetos es un trabajo tedioso y molesto.

Afortunadamente, hay contenedores de IoC , que lo hacen mucho más fácil. Además de Spring , el contenedor Guice IoC de Google es bastante popular entre los programadores de Java.

Cuando se usa un contenedor IoC, lo configura para que se comporte de cierta manera, es decir. dirá si se supone que debe construir ciertas clases y si se requiere alguna clase como dependencia, cómo debe verse la dependencia (si siempre debe ser una nueva instancia o un singleton) y el contenedor lo conecta todo.

Puede consultar este enlace para ver un ejemplo singleton con Guice.


Pros y contras de usar un contenedor de IoC

Pros

  • ahorro de presupuesto al no tener que escribir todos los métodos de fábrica usted mismo
  • (generalmente) configuración muy sencilla
  • desarrollo rápido

Contras

  • la magia de un mago, las clases están construidas de alguna manera y realmente no puedes ver cómo sucedió
  • una pequeña caída de rendimiento debido a la búsqueda de clases (las fábricas escritas manualmente serán un poco más rápidas)
Andy
fuente
1

Singleton se refiere al concepto solo una instancia y la forma de obtener acceso a la instancia (a través del método estático tan famoso getInstance () )

Pero todavía hay una instancia detrás de todo eso. Un objeto construido con una especie de acceso restringido.

En su caso, preferiría el enfoque DI (inyección de dependencia). Como el primer bloque de código que has expuesto. Solo un pequeño cambio. Inyecte el DAO a través del constructor. Para eliminar o no el setter depende de usted. Si desea proteger el controlador de los cambios en el tiempo de ejecución, elimínelo. Si desea ofrecer esa posibilidad, consérvela.

Tiene razón al usar una interfaz y ofrecer una ventana abierta para futuras implementaciones de DAO. Puede o no ser necesario. Solo toma un minuto más de trabajo, pero hace que su diseño sea flexible. Su memoria DAO es bastante común. Muy útil como simulacro en el momento de la prueba. O como implementación DAO predeterminada.

Solo una pista. Los recursos estáticos (objetos, métodos, constantes o variables) son como recursos globales. Si los globales son malos o no, es cuestión de necesidades o gustos. Sin embargo, hay defectos implícitos vinculados a ellos. Estos están relacionados con la concurrencia , la seguridad de subprocesos (en Java, no sé sobre otros idiomas), la serialización ...

Por lo tanto, sugeriría usar las estadísticas con cautela

Laiv
fuente