DAO应该是单身吗?


14

我正在开发RESTful API,我认为为我的资源使用DAO会很方便,因为尽管我计划仅使用内存来存储它们,但是我不想对使用库的任何人敞开大门,如果他们决定使用DAO的数据库实现。

我的问题是DAO是否应为单例。如果不是,则该服务将具有DAO的实例,并且看起来大致如下:

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

虽然DAO是单身人士,但我想没有什么区别,只是在第一行:

private IEventSchedulerDao dao = EventSchedulerDao.getInstance();

我仍然必须使用IEventSchedulerDao实例,但是我想所有单例都像这样工作吗?由于某些原因,我总是将单例与静态方法相关联,因此,与其使用而不是对用户可见的单例实例getInstance(),这将被隐藏,而他/她将以EventSchedulerDao.get(name)静态方式使用等等。这是东西还是仅仅是我?

那么,我应该还是不应该拥有单身DAO?

作为附带的问题,我的方法是为用户实现自己的DAO开门吗?


您可以使用IoC单例,而不要使用带有静态访问器的单例。
CodesInChaos

Answers:


10

我不会使用单例。这是公认的反模式,并且使测试变得困难。我宁愿注入一个具体的实现,并让您的服务引用一个DAO接口(允许您注入不同的实现)


1
您在最后一句话中所暗示的正是我在做的对吗?
dabadaba'6

1
您正在通过接口进行引用(是),但未注入 DAO(请注意...)
Brian Agnew

你什么意思?我有一个塞特犬,不是吗?
dabadaba'6

@dabadaba这行private IEventSchedulerDao dao = new EventSchedulerDao();是您出错的地方。的实现IEventSchedulerDao应通过构造函数注入,并且永远不要更改(即,也摆脱它setEventSchedulerDao)。
David Arno

好的我明白。我只是这样做以提供默认的DAO,而对其进行更改将是“可选的”。但是,采纳您的建议意味着要为服务提供一个不同于默认构造函数的构造函数,而且很遗憾,我不知道该如何与Jersey一起使用,因为它使用了默认构造函数。您有机会知道该怎么做吗?
dabadaba'6

4

一个d ATA 一个 CCESS Ø bject应该只有真正在应用程序中一次存在。逻辑保持不变,唯一不同的是DAO提供的方法中传入和传出的值。

考虑到这一点,显然通常发生的第一件事是将DAO实现为一个强大的单例,也就是说,当您static在工厂类上有一个方法时,例如getInstance,延迟加载DAO的实例(如果为空)并返回它。

如果语法不完全正确,请问我不是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();
    }
}

这很难测试,因为您不能在不更改UsesDao类代码的情况下换出实现。这可以通过一些猴子修补来完成,但是通常不认为是一种好习惯。

然后有一个更好的方法,弱单例模式,您不通过static方法检索实例,而是通过构造函数或setter使所有类都依赖于实例(在您EventSchedulerService使用setter注入的情况下)。

唯一的问题是,那么您需要确保所有依赖于一个类实例的类都应使用相同的实例作为其参数,这些实例仅在您的应用程序生命周期中才存在。将new在整个应用程序的DAO对象上调用一次。

显然,这很难跟踪,构造对象图是一项繁琐而烦人的工作。

幸运的是,有IoC容器,这使它变得更加容易。除了Spring之外,Google 的Guice IoC容器在Java程序员中也很流行。

使用IoC容器时,可以将其配置为以某种方式运行,即。您将告诉您应该如何构造某些类,以及是否需要将某些类作为依赖项,该依赖项的外观(无论它应该始终是新实例还是单例)以及容器将其全部连接起来。

您可以在Guice中查看此链接以获取单例示例。


使用IoC容器的优缺点

优点

  • 通过不必自己编写所有工厂方法来节省预算
  • (通常)非常简单的配置
  • 快速发展

缺点

  • 巫师的魔力,这些类是以某种方式构造的,您真的看不到它是如何发生的
  • 由于类查找而导致的性能下降(手动编写的工厂会稍快一些)

1

单例仅指一个实例的概念以及访问实例的方式(通过著名的静​​态方法getInstance())。

但所有这些背后仍然有一个实例。具有某种限制访问的已构建对象。

在您的情况下,我宁愿采用DI方法(依赖注入)。就像您公开的第一段代码一样。只是一点点变化。通过构造函数注入DAO。是否删除安装员取决于您。如果要保护Controller免受运行时更改的影响,请删除它。如果您想提供这种可能性,请保留它。

您可以正确使用接口并为DAO的进一步实现提供一个打开的窗口。可能需要,也可能不需要。它仅需花费一分钟的时间,却使您的设计变得灵活。您记忆中的DAO非常普遍。在测试时作为模拟非常有用。或作为默认的DAO实现。

只是一个提示。静态资源(对象,方法,常量或变量)就像全局资源。全球人是否邪恶是需求或品味的问题。但是,存在固有的缺点。这些与并发线程安全(在Java中,不了解其他语言),序列化有关

所以我建议谨慎使用静态

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.