Suppose you have an application that consists of two layers:
- A: A data level in which all data downloaded from a database or from a file is stored.
- B: A level that displays data in a nice user interface, for example. graph report
Now the data changes in layer A. We have 2 approaches to ensure that reports from level B are correctly updated.
The first approach is the PUSH approach. Layer A notifies level B through observers, so layer B can update its reports.
There are several drawbacks to the PUSH approach:
- If the data changes several times (for example, at boot time or in algorithms that change a lot of data), observers are executed many times. This can be solved by introducing some kind of buffering (prevent observer calls while you are still changing), but it can be very difficult, and the correct buffering calls are often forgotten.
- If a lot of data is changed, observer calls can cause overhead that is unacceptable in the application.
Another approach is the PULL approach. Layer A simply remembers what data has been changed and does not send any notifications (layer A is marked dirty). After the action that was performed by the user (an algorithm or file loading or something else can be performed), we check all the components of the user interface and ask them to update themselves. In this case, layer B is asked to update itself. First, he checks if any of its underlying layers is dirty (layer A). If so, he will receive the changes and the update itself. If layer A was not dirty, the report knew that it had nothing to do.
The best solution depends on the situation. In my situation, the PUSH approach looks much better.
The situation becomes much more complicated if we have more than 2 layers. Suppose we have the following 4 layers:
- A: A data level in which all data downloaded from a database or from a file is stored.
- B: A layer that uses a data layer (layer A), for example. to filter data from A using a sophisticated filter function
- C: a layer that uses layer B, for example. to combine data from layer B into smaller pieces of information.
- D: a report that interprets the results of layer C and presents it in a beautiful graphical way for the user
In this case, REDUCING the changes will almost certainly result in significantly higher costs.
On the other hand, for PULLING changes it is required that:
- layer D should cause layer C to ask if it is dirty
- layer C should call layer B to ask if it is dirty
- layer B must cause layer A to find out if it is dirty
If nothing has changed, the number of calls to complete before you find out that nothing has actually been changed, and you do not need to do anything, is quite large. It seems that the performance overhead that we are trying to avoid without using PUSH is now reverting to use in the PULL approach due to many calls to ask if anything is dirty.
Are there any patterns that solve this problem in a good and high performance (low waybill) way?