具有 CRUD 静态方法的观察者模式和 DAO 类

Observer pattern and DAO classes with CRUD static methods

我正在审查一些 DAO classes,它们将 CRUD 静态方法 create()、delete() 等暴露给程序,每个 DAO class 实现观察者模式通过 Notification.java class 检查数据库中的更改,以及是否通过从数据库中拉取对象收到更改通知。

简化代码类似这样:

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 class 是通过它的静态方法使用的,所以永远不会调用构造函数。

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

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

这看起来很老套,所以我想添加一个

static {
  Notification.addChangeListener(this);
}

在 OfficeDAO.java class 中,但当然 "this" 引用不存在于静态初始化程序中,所以我没有解决方案。 从 DAO 方法中删除静态是没有问题的,因为这些方法在整个应用程序中被调用,并且在没有 class.

实例的情况下被调用

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

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

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

您可以将所有 DAO classes 更改为 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(..)

*也就是说,看起来您的观察者也是主题,这不是实施观察者模式的 classic 方式。此外,您可以避免双重检查锁定并求助于未延迟实例化的单例。如何实现 Singleton 是您的选择,与问题没有直接关系。

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

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

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

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

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

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