【问题标题】:Observer pattern and DAO classes with CRUD static methods带有 CRUD 静态方法的观察者模式和 DAO 类
【发布时间】:2015-04-23 16:05:55
【问题描述】:

我正在审查一些 DAO 类,它们将 CRUD 静态方法 create()、delete() 等暴露给程序,并且每个 DAO 类通过通知检查数据库中的更改来实现观察者模式。 java 类,如果通过从数据库中提取对象接收到一个更改通知。

简化的代码类似这样:

OfficeDAO.java

public class OfficeDAO implements PropertyChangeListener
{
    public OfficeDAO()
    {
        /* 
         * Below we add ourselves to the observers of class Notifications
         * (see observer pattern in Java) in practice we are notified
         * when one of our objects is changed by a remote client user
         */
        Notification.addChangeListener(this);
    }

    public static void create(Office office)
    {
      Connection connection = DBConnection.getConnection();

      //... stuff with the database insertion

      Notification.notifyDatabaseChanges("ocreate-" + officeId);
    }
}

现在的问题是 addChangeListener(this);是在构造函数中,但因为这个 DAO 类是通过其静态方法使用的,所以从不调用构造函数。

要在应用程序的 main() 方法中解决这个问题(顺便说一下,在 EDT 线程内),有一个类似的调用:

new OfficeDAO(); //ignore this, it is needed only to start the DAO constructor

这看起来很hacky所以我想添加一个

static {
  Notification.addChangeListener(this);
}

在 OfficeDAO.java 类中,但静态初始化程序中当然不存在“this”引用,所以我没有解决方案。 从 DAO 方法中删除静态是不可能的,因为这些方法在整个应用程序中被调用,并且在没有类实例的情况下被调用。

我目前缺少任何干净的解决方案或解决方法吗?

【问题讨论】:

  • PropertyChangeListener 是否也只包含在每个 DAO 中重新定义的静态方法?你能显示 PropertyChangeListener 和 Notification 类的代码吗?此外,在经典的观察者模式中,是 Observable 通知观察者其状态发生变化

标签: java dao observer-pattern


【解决方案1】:

这似乎是一个相当混乱的场景,创建一个新对象只是为了向 Notification 添加一个侦听器似乎更像是一种反模式。我的假设是这是一些遗留代码的一部分,不可能进行太多代码重构。我可以说,由于 DAO 层更像是一个单例,你可以嵌入一个预先创建的 DAO 实例并通过静态引用访问它。

private static OfficeDAO myDAO = new OfficeDAO(); //The constructor code remains the same

【讨论】:

  • 是的,这是一些遗留代码,如果我确定这样做的方式是相当多的 DAO 并且项目本身相当大,则可以对其进行重构。如果我将 DAO 设为静态,我会破坏 Notification.addChangeListener(this);对应用程序正常工作至关重要的调用。
  • @sarah.ferguson 我认为您不需要创建 DAO 的 static。这个答案旨在解释的是,您可以使用static 引用您的DAO,而不仅仅是说new DAO();虽然这会使代码看起来不那么可怕,但它肯定会适得其反。为什么要使用类的引用来访问静态方法?您的 IDE 将在您的代码中添加有关此问题的警告。
【解决方案2】:

您可以将所有 DAO 类更改为 Singleton。我同意不需要创建 DAO 实例,因为您的 DAO 没有状态,这不是理想的解决方案。但同样,您不是在寻找理想的解决方案,而是在客户端代码中所需更改最少的更简洁的 hack。我不确定您是否在项目中使用 IoC 框架,但如果您决定将来使用一个,将 DAO 转换为 Singleton 将为相同的基础工作。

让我们将 OfficeDAO 转换为 Singleton:

public class OfficeDAO implements PropertyChangeListener {

    private static volatile OfficeDAO INSTANCE;

    private OfficeDAO() {
        if (INSTANCE != null) {// prevent reflection attacks
            throw new InstantiationError("Illegal attempt to create more than one instance of OfficeDAO");
        }
        Notification.addChangeListener(this);
    }

    public static OfficeDAO getInstance() {
        OfficeDAO localInstance = INSTANCE;
        if (INSTANCE == null) {
            synchronized (OfficeDAO.class) {
                localInstance = INSTANCE;
                if (localInstance== null) {
                    INSTANCE = localInstance = new OfficeDAO();
                }
            }
        }
        return localInstance;
    }

    public void create(Office office) {
        Connection connection = DBConnection.getConnection();

        // ... stuff with the database insertion

        Notification.notifyDatabaseChanges("ocreate-" + officeId);
    }
}

如果您以类似的方式更改所有 DAO,则您必须在客户端代码中进行的唯一更改是将 ClassName.staticMethod() 更改为 ClassName.getInstance().staticMethod()

例如:OfficeDAO.getInstance().create(..)

*话虽如此,看起来您的观察者也是主题,这不是实现观察者模式的经典方式。此外,您可以避免双重检查锁定并使用未延迟实例化的单例。如何实现 Singleton 取决于您的选择,与问题没有直接关系。

如果您的项目开始使用 Spring 或 Guice 等 Ioc 框架,您可以摆脱私有构造函数和 getInstance 方法。那么最好的办法是将 DAO 中的所有静态方法更改为实例方法,并让 IoC 框架创建 DAO 并将其注入到所有需要它们的类中。这有几个优点:

  1. 大多数 IoC 框架允许您决定是在请求时只提供一个类的对象,还是在每次请求时都提供一个新对象。因此,您可以在单例与非单例 DAO 之间进行选择,而无需更改您的 DAO。
  2. 您的数据源可以从数据库更改为 .csv 文件,您不必更改使用 DAO 的客户端代码。
  3. 实际上,您可以在服务类中模拟您的 DAO 以进行单元测试。

【讨论】:

  • 好吧,单例是一个不那么老套的解决方案,但我想提供一个“干净”的解决方案,因为我会搞砸很多类。 “双重检查锁定”是什么意思?观察者不能也是主体吗?它是反模式吗?
  • @sarah.ferguson 您将进行的更改量与您的代码的 hacky 程度成反比。你会用我的解决方案弄乱很多类,但除了客户端代码访问方法的方式之外,你不会真正改变太多。你可以阅读更多关于双重检查锁定here。您不需要以这种方式实现单例。在网站上搜索实现 Singleton 的不同方法。观察者成为主体并没有错。
【解决方案3】:

DAO 设计是一个非常复杂的主题,您可以先阅读 Balusc(本论坛用户)在 DAO design 上的文章,最后选择处理 DAO 和连接管理的框架。

Butterfly Persistence 是一个非常精简的库,它可以让您在正确的 DAO 实现方面有一个良好的开端,它会为您创建 DAOFactory 并管理连接,而无需导入 100MB 的 Spring 或 Hibernate jar 库。

至于为数据库操作添加监听器和通知器,您应该完全避免这种情况,而是使用支持事件通知的数据库,例如 Oracle 或 Firebird。

这样您只监听事件,数据库会通知您对其表的任何更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-01
    • 2014-05-15
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多