Un DAO dovrebbe essere singleton o no?


14

Sto sviluppando un'API RESTful e penso che sia conveniente usare i DAO per le mie risorse perché anche se ho intenzione di usare solo la memoria per archiviarli, non voglio chiudere una porta a chiunque stia usando la mia biblioteca se hanno deciso di usare un'implementazione di database per DAO.

La mia domanda è se il DAO dovrebbe essere un singleton o no. In caso contrario, il servizio avrà un'istanza del DAO e sarebbe simile a questo:

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

Mentre se il DAO fosse un singleton, ma immagino che non ci sarebbe molta differenza, solo nella prima riga:

private IEventSchedulerDao dao = EventSchedulerDao.getInstance();

Dovrei ancora usare IEventSchedulerDaoun'istanza, ma immagino che tutti i singoli funzionino in questo modo, giusto? Per qualche motivo, ho sempre correlato i singoli con metodi statici, quindi invece di avere un'istanza singleton visibile all'utente getInstance(), questo sarebbe nascosto e lui / lei userebbe semplicemente EventSchedulerDao.get(name), ecc ... in modo statico. È una cosa o sono solo io?

Quindi, dovrei o non dovrei avere DAO singleton?

E come domanda secondaria, va bene il mio approccio avere porte aperte affinché l'utente possa implementare i propri DAO?


È possibile utilizzare un singleton IoC anziché un singleton con un accessor statico.
CodesInChaos

Risposte:


10

Non userei un singleton. È un anti-pattern riconosciuto e rende difficile il test. Preferirei piuttosto iniettare in un'implementazione concreta e fare in modo che il tuo servizio faccia riferimento a un'interfaccia DAO (che ti consente di iniettare diverse implementazioni in)


1
Quello che suggerisci nella tua ultima frase è esattamente quello che sto facendo bene?
Dabadaba,

1
Stai facendo riferimento tramite un'interfaccia (sì), ma non stai iniettando il DAO (per essere chiari ....)
Brian Agnew,

Cosa intendi? Ho un setter per questo, no?
Dabadaba,

@dabadaba La linea private IEventSchedulerDao dao = new EventSchedulerDao();è dove hai sbagliato. L'implementazione per IEventSchedulerDaodeve essere iniettata tramite il costruttore e non deve mai essere modificata (ovvero eliminarla setEventSchedulerDao).
David Arno,

Ok capisco. L'ho appena fatto per fornire un DAO predefinito e modificarlo sarebbe "opzionale". Ma prendere il tuo suggerimento significa avere un costruttore per il servizio diverso da quello predefinito, e in fondo non ho idea di come funzioni con Jersey perché usa il costruttore predefinito. Per caso sai come farlo?
Dabadaba,

4

A D ata A ccess O bject dovrebbe esistere davvero una sola volta nella tua applicazione. La logica rimane la stessa, le uniche cose che sono diverse sono i valori che entrano ed escono dai metodi forniti da DAO.

Con questo in mente, ovviamente la prima cosa che accade di solito è l'implementazione del DAO come un singleton forte , cioè quando hai un staticmetodo su una classe factory, qualcosa di simile getInstance, pigro che carica un'istanza del DAO se è nulla e lo restituisce.

Mi scusi se la sintassi non è completamente corretta, non sono un programmatore 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();
    }
}

Questo è incredibilmente difficile da testare, perché non è possibile scambiare l'implementazione senza alterare il codice della UsesDaoclasse. Questo può essere fatto attraverso alcune patch scimmie , ma generalmente non è considerata una buona pratica.

Quindi c'è il modo migliore, il modello singleton debole , in cui non si recupera un'istanza tramite un staticmetodo, ma si fa in modo che tutte le classi dipendano dall'istanza tramite un costruttore o un setter (nel proprio caso EventSchedulerServicesi sta utilizzando l'iniezione setter).

L'unico problema è che devi assicurarti che tutte le classi, che dipendono da un'istanza di classe che dovrebbe esistere solo dopo il ciclo di vita dell'applicazione, stiano prendendo la stessa istanza del loro parametro, ad es. la newè chiamato solo una volta sull'oggetto DAO nell'intera applicazione.

Ovviamente, questo è incredibilmente difficile da tracciare e la costruzione del grafico a oggetti è un lavoro noioso e fastidioso.

Fortunatamente, ci sono contenitori IoC , che lo rendono molto più semplice. Oltre a Spring , il contenitore Guice IoC di Google è abbastanza popolare tra i programmatori Java.

Quando si utilizza un contenitore IoC, lo si configura per comportarsi in un certo modo, ad es. dite se si suppone che debba costruire determinate classi e se una classe è richiesta come dipendenza, come dovrebbe essere la dipendenza (se deve sempre essere una nuova istanza o un singleton) e il contenitore lo collega.

Puoi controllare questo link per un esempio singleton con Guice.


Pro e contro dell'utilizzo di un contenitore IoC

Professionisti

  • risparmiando budget non dover scrivere tutti i metodi di fabbrica da soli
  • (di solito) configurazione molto semplice
  • sviluppo rapido

Contro

  • la magia di un mago, le classi sono in qualche modo costruite e non puoi davvero vedere come è successo
  • un piccolo calo delle prestazioni dovuto alla ricerca di classe (le fabbriche scritte manualmente saranno leggermente più veloci)

1

Singleton si riferisce al concetto solo di un'istanza e al modo di ottenere l'accesso all'istanza (attraverso il metodo statico così famoso getInstance () )

Ma c'è ancora un esempio dietro tutto ciò. Un oggetto costruito con una sorta di accesso limitato.

Nel tuo caso, preferirei l'approccio DI (iniezione di dipendenza). Come il primo blocco di codice che hai esposto. Solo un piccolo cambiamento. Iniettare il DAO tramite il costruttore. Rimuovere o meno il setter dipende da te. Se si desidera proteggere il Controller dalle modifiche nel runtime, rimuoverlo. Se vuoi offrire tale possibilità, allora tienilo.

Hai ragione a utilizzare un'interfaccia e ad offrire una finestra aperta per ulteriori implementazioni di DAO. Potrebbe non essere necessario. Ci vuole solo un minuto in più di lavoro, ma rende flessibile il tuo design. Il tuo DAO in memoria è abbastanza comune. Molto utile come finto al momento del test. O come implementazione DAO predefinita.

Solo un suggerimento. Le risorse statiche (oggetti, metodi, costanti o variabili) sono come risorse globali. Se i globuli sono cattivi o no è questione di bisogni o gusti. Tuttavia ci sono implicazioni carenze legate a loro. Questi sono correlati a concorrenza , sicurezza dei thread (in Java, non conosco altre lingue), serializzazione ...

Quindi suggerirei di usare la statica con cautela

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.