Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OOP circular reference

Tags:

java

oop

I have a dilemma here about what is and what is not a circular reference... and how to get rid of it in affirmative case.

Let's say I have a Main class that instantiates two different classes where each of these classes need to execute a method in the other:

MyMain {
    AlarmManager alarmManager;
    DatabaseManager databaseManager;

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

    AlarmManager getAlarmManager() {
        return alarmManager;
    }

    DatabaseManager getDatabasetManager() {
        return databaseManager;
    }
}

And the classes:

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

I suppose you get the idea by looking at the code. If there's an alarm we receive it in the AlarmManager, but it needs to save it in the historical database. However, if we have a connection error with the historical database, we need to also send an alarm.

Is this really a circular reference, where main has alarm but alarm also has main and the same for database/main? How would you solve it?

like image 939
daniel sp Avatar asked Nov 02 '22 05:11

daniel sp


1 Answers

I had a similar problem when did a CAD development. In every Electronic CAD you have some component ports and signal lines that represent connections between the ports. The lines and ports are many-to-many connections: every line must maintain a list of ports that it binds together as well as the ports must beware of the lines, attached to them. So, every time user wants to connect two ports, he creates a line and attaches the line to each port by

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

These two operations must always be executed together. This means they must be abstracted as a procedure: calling line.add(port) would automatically call port.add(line) and, by symmetry, port.add(line) should call line.add(port). Well, we are in the vicious circle, exactly the one you have in your question!

I bothered me for years but I did nothing better than giving up the symmetry. I have exposed only line.add to the user, hiding the port.add, that it calls, behind my netlist manipulation API. This way, port.add does not need to call the line.add back since we know that we are in port.add only because line.add is already happening.

I could restore the symmetry by introducing public port.add and line.add, which would call API-private line.onAdd(port) and port.onAdd(line) callbacks. This also seems trivial. Yet, you are right asking this question. I encourage you to upvote my response but not accept it. I would like to know the correct (OOP) answer myself.

like image 132
Val Avatar answered Nov 15 '22 03:11

Val