Un DAO doit-il être singleton ou non?

14

Je suis en train de développer une API RESTful et je pense qu'il est pratique d'utiliser des DAO pour mes ressources, car même si je prévois d'utiliser uniquement de la mémoire pour les stocker, je ne veux pas fermer la porte à quiconque utilise ma bibliothèque s'il décide d'utiliser une implémentation de base de données pour le DAO.

Ma question est de savoir si le DAO doit être un singleton ou non. Si ce n'est pas le cas, le service aura une instance du DAO et il ressemblerait à peu près à ceci:

@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);
    }
}

Alors que si le DAO était un singleton, mais je suppose qu'il n'y aurait pas beaucoup de différence, juste dans la première ligne:

private IEventSchedulerDao dao = EventSchedulerDao.getInstance();

Je devrais encore utiliser une IEventSchedulerDaoinstance, mais je suppose que tous les singletons fonctionnent comme ça, non? Pour une raison quelconque, je corrèle toujours les singletons aux méthodes statiques, donc au lieu d'avoir une instance de singleton visible par l'utilisateur getInstance(), cela serait caché et il / elle utiliserait simplement EventSchedulerDao.get(name), etc ... de manière statique. Est-ce une chose ou est-ce juste moi?

Alors, dois-je ou ne dois-je pas avoir des DAO singleton?

Et comme question secondaire, est-ce que mon approche est d'avoir des portes ouvertes pour que l'utilisateur implémente ses propres DAO?

dabadaba
la source
Vous pouvez utiliser un singleton IoC au lieu d'un singleton avec un accesseur statique.
CodesInChaos

Réponses:

10

Je n'utiliserais pas de singleton. C'est un anti-modèle reconnu et rend les tests difficiles. Je préfère de loin injecter dans une implémentation concrète, et demander à votre service de référencer une interface DAO (vous permettant d'injecter différentes implémentations dans)

Brian Agnew
la source
1
Ce que vous suggérez dans votre dernière phrase est précisément ce que je fais bien?
dabadaba du
1
Vous faites un référencement via une interface (oui), mais vous n'injectez pas le DAO (pour être clair ....)
Brian Agnew
Que voulez-vous dire? J'ai un passeur pour ça, non?
dabadaba
@dabadaba La ligne private IEventSchedulerDao dao = new EventSchedulerDao();est l'endroit où vous vous êtes trompé. L'implémentation de IEventSchedulerDaodoit être injectée via le constructeur et ne doit jamais être modifiée (c'est-à-dire se débarrasser de setEventSchedulerDaotrop).
David Arno
Ok, je comprends. Je viens de le faire pour fournir un DAO par défaut et le changer serait "facultatif". Mais prendre votre suggestion signifie avoir un constructeur pour le service différent de celui par défaut, et je ne sais absolument pas comment cela fonctionne avec Jersey car il utilise le constructeur par défaut. Par hasard, savez-vous comment faire cela?
dabadaba
4

A D ata A ccès O bjet ne devrait exister vraiment une fois dans votre application. La logique reste la même, les seules choses qui sont différentes sont les valeurs entrant et sortant des méthodes fournies par le DAO.

Dans cet esprit, la première chose qui se produit est évidemment d'implémenter le DAO en tant que singleton fort , c'est-à-dire lorsque vous avez une staticméthode sur une classe d'usine, quelque chose comme getInstance, paresseux charger une instance du DAO si elle est nulle et la renvoyer.

Excusez-moi si la syntaxe n'est pas tout à fait correcte, je ne suis pas un programmeur 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();
    }
}

C'est incroyablement difficile à tester, car vous ne pouvez pas échanger l'implémentation sans modifier le code de la UsesDaoclasse. Cela peut être fait grâce à quelques correctifs de singe , mais n'est généralement pas considéré comme une bonne pratique.

Ensuite, il y a la meilleure façon, le modèle singleton faible , où vous ne récupérez pas une instance via une staticméthode, mais faites en sorte que toutes les classes dépendent de l'instance via un constructeur ou un setter (dans votre cas, EventSchedulerServicevous utilisez l'injection de setter).

Le seul problème est que vous devez alors vous assurer que toutes les classes, qui dépendent d'une instance de classe qui ne devrait exister qu'une fois le cycle de vie de votre application, prennent la même instance que leur paramètre, c'est-à-dire. l' newest appelée une seule fois sur l'objet DAO dans l'application.

De toute évidence, cela est incroyablement difficile à suivre et la construction du graphique d'objet est un travail fastidieux et ennuyeux.

Heureusement, il existe des conteneurs IoC , ce qui le rend beaucoup plus facile. Outre Spring , le conteneur Guice IoC de Google est très populaire parmi les programmeurs Java.

Lorsque vous utilisez un conteneur IoC, vous le configurez pour se comporter d'une certaine manière, c'est-à-dire. vous dites si la façon dont elle est censée construire certaines classes et si une classe est requise en tant que dépendance, à quoi devrait ressembler la dépendance (que ce soit toujours une nouvelle instance ou un singleton) et le conteneur le relie.

Vous pouvez vérifier ce lien pour un exemple singleton avec Guice.


Avantages et inconvénients de l'utilisation d'un conteneur IoC

Avantages

  • économiser du budget en n'ayant pas à écrire toutes les méthodes d'usine vous-même
  • (généralement) configuration très simple
  • développement rapide

Les inconvénients

  • la magie d'un sorcier, les classes sont en quelque sorte construites et vous ne pouvez pas vraiment voir comment cela s'est passé
  • une petite baisse des performances due à la recherche de classe (les usines écrites manuellement seront légèrement plus rapides)
Andy
la source
1

Singleton se réfère au concept d' une seule instance et à la façon d'accéder à l'instance (via la fameuse méthode statique getInstance () )

Mais il y a toujours un exemple derrière tout cela. Un objet construit avec une sorte d'accès restreint.

Dans votre cas, je préférerais une approche DI (injection de dépendance). Comme le premier bloc de code que vous avez exposé. Juste un petit changement. Injectez le DAO via le constructeur. C'est à vous de retirer ou non le passeur. Si vous souhaitez protéger le contrôleur des modifications de l'exécution, supprimez-le. Si vous voulez offrir une telle possibilité, gardez-la.

Vous avez raison d'utiliser une interface et d'offrir une fenêtre ouverte pour d'autres implémentations de DAO. Peut ou peut ne pas être nécessaire. Cela ne prend qu'une minute de plus, mais cela rend votre conception flexible. Votre DAO en mémoire est assez courant. Très utile comme maquette au moment des tests. Ou comme implémentation DAO par défaut.

Juste un indice. Les ressources statiques (objets, méthodes, constantes ou variables) sont comme des ressources globales. Si les globaux sont mauvais ou non, c'est une question de besoins ou de goûts. Cependant, il y a des lacunes implicites qui leur sont liées. Ceux-ci sont liés à la concurrence , à la sécurité des threads (en Java, je ne connais pas les autres langages), à la sérialisation ...

Je suggère donc d'utiliser la statique avec prudence

Laiv
la source