Um DAO deve ser único ou não?


14

Estou desenvolvendo uma API RESTful e acho conveniente usar DAOs para meus recursos porque, embora planeje apenas usar a memória para armazená-los, não quero fechar uma porta para quem estiver usando minha biblioteca, se eles decidirem usar uma implementação de banco de dados para o DAO.

Minha pergunta é se o DAO deve ser um singleton ou não. Caso contrário, o serviço terá uma instância do DAO e seria mais ou menos assim:

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

Embora o DAO fosse um singleton, mas acho que não haveria muita diferença, apenas na primeira linha:

private IEventSchedulerDao dao = EventSchedulerDao.getInstance();

Eu ainda teria que usar uma IEventSchedulerDaoinstância, mas acho que todos os singletons funcionam assim, certo? Por alguma razão, eu sempre correlaciono singletons a métodos estáticos; portanto, em vez de ter uma instância singleton visível para o usuário getInstance(), isso estaria oculto e ele / ela usaria EventSchedulerDao.get(name), etc ... de maneira estática. Isso é uma coisa ou sou apenas eu?

Então, devo ou não ter DAOs singleton?

E, como uma questão paralela, está bem minha abordagem ter portas abertas para o usuário implementar seus próprios DAOs?


Você pode usar um singleton IoC em vez de um singleton com um acessador estático.
CodesInChaos

Respostas:


10

Eu não usaria um singleton. É um anti-padrão reconhecido e dificulta o teste. Prefiro injetar uma implementação concreta e fazer com que seu serviço faça referência a uma interface DAO (permitindo que você injete implementações diferentes)


1
O que você sugere na sua última frase é exatamente o que estou fazendo certo?
dabadaba

1
Você está fazendo referência através de uma interface (sim), mas você não está injetando o DAO (para ser claro ....)
Brian Agnew

O que você quer dizer? Eu tenho um setter para isso, não tenho?
dabadaba

@dabadaba A linha private IEventSchedulerDao dao = new EventSchedulerDao();é onde você errou. A implementação IEventSchedulerDaodeve ser injetada através do construtor e nunca alterada (isto é, livrar-se setEventSchedulerDaotambém).
David Arno

OK, eu entendo. Eu apenas fiz isso para fornecer um DAO padrão e alterá-lo seria "opcional". Mas aceitar sua sugestão significa ter um construtor para o serviço diferente do padrão e, sinceramente, não tenho idéia de como isso funciona com Jersey, porque ele usa o construtor padrão. Por acaso você sabe como fazer isso?
precisa saber é o seguinte

4

A D ata A cesso O bject realmente só deve existir uma vez na sua aplicação. A lógica permanece a mesma, as únicas coisas diferentes são os valores que entram e saem dos métodos fornecidos pelo DAO.

Com isso em mente, obviamente, a primeira coisa que normalmente acontece é implementar o DAO como um singleton forte , ou seja, quando você tem um staticmétodo em uma classe de fábrica, algo como getInstance, carregar preguiçosamente uma instância do DAO se ele for nulo e retorná-lo.

Com licença, se a sintaxe não estiver totalmente correta, eu não sou um programador 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();
    }
}

Isso é incrivelmente difícil de testar, porque você não pode trocar a implementação sem alterar o código da UsesDaoclasse. Isso pode ser feito através de alguns remendos de macacos , mas geralmente não é considerado uma boa prática.

Depois, existe a melhor maneira, o padrão singleton fraco , em que você não recupera uma instância por meio de um staticmétodo, mas faz com que todas as classes dependam da instância por meio de um construtor ou de um setter (no seu EventSchedulerServiceuso da injeção de setter).

O único problema é que você precisa garantir que todas as classes, que dependem de uma instância da classe que só deve existir uma vez que o ciclo de vida do aplicativo, estejam executando a mesma instância que o parâmetro, por exemplo. o newé chamado apenas uma vez no objeto DAO em todo o aplicativo.

Obviamente, isso é incrivelmente difícil de rastrear e construir o gráfico de objetos é um trabalho tedioso e irritante.

Felizmente, existem recipientes de IoC , o que facilita muito. Além de Spring , o contêiner Guice IoC do Google é bastante popular entre os programadores de Java.

Ao usar um contêiner de IoC, você o configura para se comportar de uma certa maneira, ou seja. você diz se como deve construir determinadas classes e se é necessária alguma classe como uma dependência, como a dependência deve parecer (se sempre deve ser uma nova instância ou um singleton) e o contêiner conecta tudo.

Você pode verificar este link para um exemplo único com o Guice.


Prós e contras do uso de um contêiner de IoC

Prós

  • economizando orçamento ao não precisar escrever todos os métodos de fábrica
  • (geralmente) configuração muito direta
  • desenvolvimento rápido

Contras

  • mágica de um mago, as aulas são de alguma forma construídas e você não consegue ver como isso aconteceu
  • uma pequena queda de desempenho devido à pesquisa de classe (fábricas escritas manualmente serão um pouco mais rápidas)

1

Singleton está se referindo ao conceito apenas uma instância e a maneira de obter acesso à instância (por meio do famoso método estático getInstance () )

Mas ainda há um exemplo por trás de tudo isso. Um objeto construído com uma espécie de acesso restrito.

No seu caso, eu preferiria a abordagem DI (injeção de dependência). Como o primeiro bloco de código que você expôs. Apenas uma pequena mudança. Injete o DAO via construtor. Para remover ou não, a incubadora é sua. Se você deseja proteger o Controller contra alterações no tempo de execução, remova-o. Se você deseja oferecer essa possibilidade, mantenha-a.

Você está certo em usar uma interface e oferecer uma janela aberta para futuras implementações do DAO. Pode ou não ser necessário. Demora apenas mais um minuto de trabalho, mas torna seu design flexível. O seu DAO na memória é bastante comum. Muito útil como simulação no momento do teste. Ou como implementação padrão do DAO.

Apenas uma dica. Recursos estáticos (objetos, métodos, constantes ou variáveis) são como recursos globais. Se os globais são maus ou não, é uma questão de necessidades ou gostos. No entanto, existem deficiências implícitas ligadas a eles. Eles estão relacionados à simultaneidade , segurança de threads (em Java, não conhece outras linguagens), serialização ...

Então eu sugiro usar a estática com cautela

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.