Предположим, у вас есть приложение, состоящее из двух слоев:
- A: слой данных, в котором хранятся все данные, загруженные из базы данных или из файла
- B: Aслой, который отображает данные в удобном пользовательском интерфейсе, например, в графическом отчете
. Теперь данные изменяются в слое A. У нас есть 2 подхода, чтобы убедиться, что отчеты со слоя B корректно обновляются.
Первый подход - это PUSH-подход.Уровень A уведомляет уровень B через наблюдателей, чтобы уровень B мог обновлять свои отчеты.
У подхода PUSH есть несколько недостатков:
- Если данные меняются несколько раз (например, во время загрузки илив алгоритмах, которые изменяют много данных), наблюдатели выполняются много раз.Эту проблему можно решить, введя своего рода буферизацию (не вызывайте наблюдателей, пока вы все еще изменяетесь), но это может быть очень сложно, и правильные буферизационные вызовы часто забываются.
- Если изменяется большое количество данных,вызовы наблюдателя могут вызвать накладные расходы, которые неприемлемы в приложении.
Другой подход - подход PULL.Слой A просто запоминает, какие данные были изменены, и не отправляет никаких уведомлений (слой A помечен как грязный).После действия, которое было выполнено пользователем (может быть запуск алгоритма или загрузка файла или что-то еще), мы проверяем все наши компоненты пользовательского интерфейса и просим их обновить себя.В этом случае уровень B запрашивается для обновления.Сначала он проверит, не загрязнен ли какой-либо из его нижележащих слоев (слой A).Если это так, он получит изменения и обновится сам.Если слой А не был грязным, отчет знал, что он не имеет ничего общего.
Лучшее решение зависит от ситуации.В моей ситуации подход PUSH кажется намного лучше.
Ситуация становится намного сложнее, если у нас более двух слоев.Предположим, у нас есть следующие 4 слоя:
- A: слой данных, в котором хранятся все данные, загруженные из базы данных или из файла
- B: слой, который использует слой данных(уровень A), например, для фильтрации данных из A с использованием сложной функции фильтрации
- C: уровень, который использует уровень B, например, для объединения данных из уровня B в более мелкие фрагменты информации
- D: отчет, который интерпретирует результаты уровня C и представляет его в хорошем графическом виде для пользователя
В этом случае нажатие на изменения почти наверняка приведет к гораздо более высоким издержкам.
С другой стороны, для того, чтобы вытащить изменения, необходимо:
- уровень D должен вызвать уровень C, чтобы спросить, является ли он грязным
- уровень C должен вызвать уровень B дляспросить, является ли он грязным
- слой B должен вызвать слой A, чтобы спросить, не загрязнен ли он
Если ничего не изменилось, количество вызовов, которые нужно выполнить, прежде чем вы узнаете, что на самом деле ничегобыл изменен, и вы не чAve, чтобы сделать что-нибудь довольно большое.Похоже, что производительность, которую мы пытаемся избежать, не используя PUSH, теперь возвращается к использованию в подходе PULL из-за множества вызовов, чтобы спросить, не является ли что-то грязным.
Существуют ли шаблоны, которые решают проблему?такая проблема в хорошем и высокопроизводительном (низком объеме) способе?