2014-02-18 15 views
5

Ich habe hier ein Dilemma darüber, was ist und was kein Zirkelbezug ist ... und wie man es im affirmativen Fall los wird.OOP-Zirkelreferenz

Sagen wir, ich habe eine Main Klasse, die zwei verschiedene Klassen instanziiert wo jede dieser Klassen müssen ein Verfahren, in dem anderen auszuführen:

MyMain { 
    AlarmManager alarmManager; 
    DatabaseManager databaseManager; 

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

    AlarmManager getAlarmManager() { 
     return alarmManager; 
    } 

    DatabaseManager getDatabasetManager() { 
     return databaseManager; 
    } 
} 

Und die Klassen:

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

Ich nehme an Sie erhalten die Idee, indem Sie sich den Code ansehen. Wenn es einen Alarm gibt, erhalten wir ihn im AlarmManager, aber er muss ihn in der historischen Datenbank speichern. Wenn jedoch ein Verbindungsfehler mit der historischen Datenbank vorliegt, müssen wir auch einen Alarm senden.

Ist dies wirklich eine zirkuläre Referenz, wo Haupt Alarm hat aber Alarm hat auch Haupt-und das gleiche für Datenbank/Haupt? Wie würdest du es lösen?

+1

Es sieht wie ein Designfehler aus, können Sie die Klassen näher erläutern und was sie tun sollen? Vielleicht können wir uns ein anderes Design ausdenken –

+0

Wie löst man Ehe OO? Ich liebe die Frage! Ich bin sicher, dass es viele (akademische) Lösungen gibt, aber ich hätte (pragmatisch) eine Basisklasse für Ereignis und Alarm. – halfbit

+0

@BartlomiejLewandowski "Designfehler" oder Knoten im Gehirn, Sie haben Recht mit "es gibt ein Bedürfnis, ein paar Schritte zurück zu gehen", denke ich – halfbit

Antwort

2

Ich hatte ein ähnliches Problem, wenn eine CAD-Entwicklung. In jedem Electronic CAD haben Sie einige Komponentenanschlüsse und Signalleitungen, die Verbindungen zwischen den Anschlüssen darstellen. Die Zeilen und Ports sind Viele-zu-Viele-Verbindungen: Jede Zeile muss eine Liste von Ports enthalten, die sie miteinander verbindet, und die Ports müssen sich vor den an ihnen angehängten Zeilen hüten. So will jeder Zeit Benutzer zwei Ports verbinden, schafft er eine Linie und legt die Linie zu jedem Port durch

line.add(port) 
port.add(line) 

Diese beiden Vorgänge müssen immer zusammen ausgeführt werden. Dies bedeutet, dass sie als eine Prozedur abstrahiert werden müssen: Rufen Sie auf, rufen Sie automatisch port.add(line) auf und rufen Sie port.add(line)line.add(port) auf. Nun, wir sind im Teufelskreis, genau der, den du in deiner Frage hast!

Ich habe mich jahrelang gestört, aber ich tat nichts besser als die Symmetrie aufzugeben. Ich habe nur dem Benutzer line.add ausgesetzt, versteckt die port.add, die es ruft, hinter meiner Netzlistenmanipulations-API. Auf diese Weise muss port.add nicht die line.add zurückrufen, da wir wissen, dass wir in port.add sind, nur weil line.add bereits passiert.

Ich konnte die Symmetrie durch die Einführung von öffentlichen port.add und line.add, wiederherzustellen, die API-private line.onAdd(port) und port.onAdd(line) Rückrufe nennen würde. Dies erscheint auch trivial. Aber Sie haben diese Frage richtig gestellt. Ich ermutige Sie, meine Antwort zu verbessern, aber nicht zu akzeptieren. Ich würde gerne die richtige (OOP) Antwort selbst wissen.

+0

Danke für Ihre Antwort. Ich würde gerne die richtige OOP-Antwort wissen ... –

0

Versuchen Sie folgendes:

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

Ihre Alarmmanager-Klasse hat eine setDatabaseManager Methode.

Ihre Datenbankmanager-Klasse verfügt über eine setAlarmManager-Methode.

0

Gilbert hat einen guten Ansatz, aber eine Alternative ist, dass sowohl der AlarmManager als auch der DatenbankManager einen Verweis auf MyMainstatt voneinander haben. z.B.

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. Vorteil: keine Setter erforderlich ist, können Sie Ihre Klassen unveränderlich sein.
  2. Vorteil: Einfach und pragmatisch.
  3. Nachteil, der ein Vorteil sein könnte: Größere Kopplung. Wenn Sie in Zukunft einen FooManager und einen BarManager hinzufügen, die AlarmManager und DatabaseManager benötigen, können sie auch in MyMain verwendet werden, was Ihr Leben enorm vereinfacht. Dies trägt jedoch eindeutig zur Kopplung und "Globalität" bei, so dass dies zu viel schlecht wäre. Wenn Sie über "einfach und pragmatisch" zu "Hier ist der Küchenspüle" gehen, sollten Sie aufhören und überdenken.
  4. Nachteil, dass mir egal ist, weil ich denke, es ist ein armes Gesetz: verstößt gegen Demeter Gesetz. Aber es ist leicht zu ändern, um dies zu vermeiden, wenn Sie wirklich interessiert sind.