OOP循环参考

时间:2014-02-18 17:20:06

标签: java oop

我在这里遇到的困境是什么是什么,什么不是循环参考......以及如何在肯定的情况下摆脱它。

假设我有一个Main类来实例化两个不同的类,其中每个类都需要在另一个类中执行一个方法:

MyMain {
    AlarmManager alarmManager;
    DatabaseManager databaseManager;

    MyMain() {
        alarmManager = new AlarmManager(dataBaseManager);
        databaseManager = new DatabaseManager (alarmManager);
    }

    AlarmManager getAlarmManager() {
        return alarmManager;
    }

    DatabaseManager getDatabasetManager() {
        return databaseManager;
    }
}

班级:

AlarmManager {
    DatabaseManager dataBaseManager;

    onAlarm(alarm) {
        dataBaseManager.saveInHistorical(alarm);
        sendAlarm(alarm);
    }

    sendAlarm(alarm) {
        socketWriter(alarm);
    }
}

DatabaseManager{
    AlarmManager alarmManager;

    onDatabaseConnectionError() {
        saveInHistorical(databaseAlarm);
        alarmManager.sendAlarm(databaseAlarm);
    }

    saveInHistorical(historical) {
        connection.store(historical);
    }
}

我想你通过查看代码得到了这个想法。如果有警报,我们会在AlarmManager中收到警报,但需要将其保存在历史数据库中。但是,如果我们与历史数据库存在连接错误,我们还需要发送警报。

这真的是一个循环引用,其中main有报警但是报警也有主要和数据库/ main相同?你会如何解决它?

3 个答案:

答案 0 :(得分:2)

CAD开发时遇到了类似的问题。在每个电子CAD中,您都有一些组件端口和信号线,用于表示端口之间的连接。线路和端口是多对多连接:每条线路必须保持一个绑定在一起的端口列表,并且端口必须注意连接到它们的线路。因此,每次用户想要连接两个端口时,他都会创建一条线并通过

将线连接到每个端口
line.add(port)
port.add(line)

这两项操作必须始终一起执行。这意味着它们必须被抽象为一个过程:调用line.add(port)会自动调用port.add(line),并且通过对称,port.add(line)应该调用line.add(port)。好吧,我们处于恶性循环中,正是你在问题中所拥有的那个!

多年来我一直困扰着我,但我没有做任何比放弃对称更好的事情。我只向用户公开了line.add,隐藏了它所调用的port.add,在我的网表操作API之后。这样,port.add不需要再调用line.add,因为我们知道我们只在port.add,因为line.add已经发生了。

我可以通过引入公开port.addline.add来恢复对称性,这会调用API私有line.onAdd(port)port.onAdd(line)回调。这似乎也是微不足道的。然而,你是对的问这个问题。我鼓励你提出我的回应,但不接受。我想知道正确的(OOP)答案。

答案 1 :(得分:0)

试试这个:

MyMain() {
    alarmManager = new AlarmManager();
    databaseManager = new DatabaseManager();
    alarmManager.setDatabaseManager(databaseManager);
    databaseManager.setAlarmManager(alarmManager);
}

您的alarmManager类有一个setDatabaseManager方法。

您的databaseManager类有一个setAlarmManager方法。

答案 2 :(得分:0)

Gilbert有一个很好的方法,但另一种方法是让alarmManager和databaseManager都引用彼此的MyMain 而不是 e.g。

AlarmManager {
    MyMain myMain;  // set in the constructor, could be final

    onAlarm(alarm) {
        myMain.getDataBaseManager().saveInHistorical(alarm);
        sendAlarm(alarm);
    }

    sendAlarm(alarm) {
        socketWriter(alarm);
    }
}

DatabaseManager{
    MyMain myMain;  // set in the constructor, could be final

    onDatabaseConnectionError() {
        saveInHistorical(databaseAlarm);
        myMain.getAlarmManager().sendAlarm(databaseAlarm);
    }

    saveInHistorical(historical) {
        connection.store(historical);
    }
}
  1. 优势:不需要setter,你的类可以是不可变的。
  2. 优势:简单实用。
  3. 可能是优势的缺点:更大的耦合。将来,当您添加AlarmManager和DatabaseManager需要的FooManager和BarManager时,它们也可以在MyMain中,极大地简化您的生活。然而,这肯定会增加耦合和“全球性”,因此太多这将是一件坏事。如果你超越“简单实用”到“这是厨房水槽”,你应该停下来重新思考。
  4. 我不关心原因的缺点我认为这是一个很差的法律:违反了得墨忒耳法则。但如果你真的在意,它很容易修改以避免这种情况。