Как избежать бесконечного цикла в паттерне Observer? - PullRequest
4 голосов
/ 17 марта 2010

У меня только один класс со многими экземплярами. Каждый экземпляр является наблюдателем нескольких других экземпляров. Кроме того, каждый экземпляр может наблюдаться несколькими другими экземплярами.

Как избежать бесконечного цикла вызова update () у наблюдателей?

Ответы [ 5 ]

7 голосов
/ 17 марта 2010

Если ваша система однопоточная, то вам просто нужен защитный элемент внутри вашего метода уведомления:

private boolean _notifying;

public void notify() {
  if(_notifying) {
    return;
  }
  _notifying = true;
  try {
    // ... do notifying here...
  } finally {
    _notifying = false;
  }
}
4 голосов
/ 17 марта 2010

То, что вы ищете, это алгоритм обхода графа, который обнаруживает циклы. Один простой подход (который работает только в однопотоковом сценарии) состоит в том, чтобы сохранить глобальный / статический счетчик, чтобы каждый вызов верхнего уровня update() получал уникальный идентификатор. Затем каждый наблюдатель отслеживает, обработал ли уже обновление с заданным идентификатором (ID), и в этом случае игнорирует его. Это означает, что ваш update метод должен быть расширен параметром с идентификационным номером конкретного обновления.

4 голосов
/ 17 марта 2010

Что ж, если вы определяете объект "событие", вы можете добавить к нему объекты, которые уже обработали событие. В этом случае, если вы закроете цикл, вы можете выйти. В seudo-код

eventFired(Event e)
  if (e.hasBeenEvaluatedBy(this)){
    return;
  }
  e.addEvaluator(this);

  // Do magic

  refire(e);
}

В этом случае мы получаем что-то вроде: * А что-то зажигает. * B обрабатывает его и добавляет себя в список * B refires * C обрабатывает событие и добавляет себя в список * C refires * А обрабатывает событие и добавляет себя в список * Refires * B ловит событие, но уже есть в списке. Без рефира, разрыв бесконечного цикла

Идентификаторы могут использоваться вместо указателей, чтобы избежать проблем со сборкой мусора

2 голосов
/ 17 марта 2010

Пусть ваши наблюдатели добавляют полученные события (или их идентификаторы) во временное хранилище, а при получении каждого нового события проверяют, сохраняется ли то же самое событие в хранилище. Если это так, то они не должны справиться с этим.

Но если мы попытаемся решить проблему, а не найдем подходящий обходной путь, тогда: ваша проблема в том, что объект A может прослушивать объект B, а объект B может одновременно прослушивать объект A (возможно, с некоторым посредником). объекты). Это плохой дизайн ИМХО.

Наблюдатели должны быть использованы для ослабления связи, чтобы объект A знал об объекте B, но не наоборот.

Если ваши объекты A и B оба знают друг о друге, тогда я не понимаю, почему вам нужно использовать наблюдателей.

0 голосов
/ 17 марта 2010

Один из способов - запускать события, например обновленные, только если что-то действительно обновлено. Преимущество этого подхода заключается в том, что вы часто можете уменьшить количество инициируемых событий (сокращение работы) и упростить код. например,

final Set<String> set = ...

public void onAdded(String string) {
   // is only added once.
   if (set.add(string)) {
       // only notifies once no matter how many times onAdded is 
       // called for this string, recursively or not.
       notifyAdded(string);
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...