2014-02-18 59 views
5

我在这里有一个两难的问题:什么是和什么不是循环引用......以及如何在肯定的情况下摆脱它。面向对象循环参考

比方说,我有一个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中接收它,但它需要将其保存在历史数据库中。但是,如果我们与历史数据库存在连接错误,则还需要发送警报。

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

+1

它看起来像一个设计缺陷,你能详细说明这些类,以及它们应该做什么?也许我们将能够拿出不同的设计 –

+0

如何解决婚姻OO?我喜欢这个问题!我确信有很多(学术)解决方案,但我会(实用的)为事件和警报设置一个基类。 – halfbit

+0

@BartlomiejLewandowski'设计缺陷'或结在大脑中,你是正确的“有需要去一些退后”,我认为 – halfbit

回答

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)答案。

+0

感谢您的回答。我想知道正确的OOP答案... –

0

试试这个:

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

你alarmManager类有一个setDatabaseManager方法。

你的databaseManager类有一个setAlarmManager方法。

0

吉尔伯特具有一个好的方法,但一种替代方法是同时具有alarmManager和的DatabaseManager必须MyMain代替彼此的的参考。 例如

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. 优势:无需制定者,你的类可以是不可改变的。
  2. 优点:简单实用。
  3. 缺点可能是一个优点:更好的耦合。将来,当你添加一个FooManager和一个BarManager以及AlarmManager和DatabaseManager需要的BarManager时,它们也可以在MyMain中,极大地简化你的生活。然而,这肯定会增加耦合度和“全局性”,所以这太多了,这将是一件坏事。如果你超越“简单而务实”到“这里是厨房水槽”,你应该停下来重新思考。
  4. 我不关心的缺点因为我认为这是一个不好的法律:违反德米特法。但它很容易修改,以避免这一点,如果你真的很在意。