Бесконечные циклы могут происходить только в том случае, если (а) Наблюдатели также являются Наблюдаемыми, (б) изменения, которые они наблюдают, могут привести к изменениям в самих себе, (в) график наблюдений является циклическим, и (г) существует вид изменений, может в конечном итоге вызвать изменение того же рода. Идеальным решением было бы определить риск бесконечных циклов, гарантируя, что одно из этих требований отсутствует. Если ваш текущий дизайн делает все четыре верными, посмотрите, можете ли вы изменить его, чтобы сделать один из них ложным.
Традиционное использование Observer-Observable в многоуровневых архитектурах - например, когда контроллеры представления наблюдают за объектами модели или когда обработчики событий наблюдают компоненты GUI - и здесь график не будет циклическим, поэтому нет риска бесконечности цикл.
Я, наверное, должен объяснить о пункте (d), о различных видах изменений. Я имею в виду, что если у вас есть ситуация, когда, скажем, UserInputEvent может вызвать ModelStateChangedEvent, а ModelStateChangedEvent может вызвать WidgetUpdateEvent, который сам по себе ничего не может запустить, то даже если наблюдатели образуют циклический граф, вы никогда не сможете получить бесконечные циклы, потому что есть только конечное количество этапов в последовательности событий. По сути, события образуют ациклический граф, даже если наблюдатели этого не делают. Если, однако, ModelStateChangedEvent может вызвать другое ModelStateChangedEvent, то у вас есть риск циклов.
Если вы действительно не можете избежать риска циклов, то вы можете украсть идею у Jon Postel и сделать так, чтобы каждое уведомление о событии содержало целочисленный счетчик времени жизни. Когда Observable транслирует «оригинальное» событие, означающее что-то, что приходит извне сети наблюдателей и запускает каскад событий внутри него, он устанавливает счетчик на некоторое подходящее начальное значение TTL. Когда Observable реагирует на событие, передавая другое событие, он использует TTL на единицу меньше, чем у инициирующего события. Когда наблюдатель получает уведомление с TTL, равным нулю, он игнорирует его. Это предотвратит бесконечные циклы, но также помешает Наблюдателю «правильно» реагировать на некоторые события, поэтому эту идею следует использовать с осторожностью. Я настоятельно рекомендую, чтобы каскад событий, достигающий предела TTL, рассматривался как результат ошибки программирования и регистрировался и передавался так же, как если бы вы обрабатывали что-то вроде NullPointerException или ошибки подтверждения.