Правильная связь для нескольких слушателей, которым необходимо получить доступ к общим данным состояния.

Работа с традиционной моделью обратного вызова слушателя. У меня есть несколько слушателей, которые собирают разные вещи. Собранный материал каждого слушателя находится внутри слушателя во внутренних структурах.

Проблема в том, что я хочу, чтобы некоторые из слушателей знали о некоторых «материалах» других слушателей.

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

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

Я начинаю понимать, как это плохо, и мне было интересно, бывало ли это раньше. Что было бы более подходящим шаблоном, когда одному из слушателей нужно получить доступ к материалам другого?

Вот некоторый псевдокод для иллюстрации:

interface Listener { onEvent(char e); }

class A implements Listener {
  private int count;
  public void onEvent(char  e) { if(e == 'a') count++; }
  public int getCount() { return count; }
}

class B implements Listener {
 private int count;
 // private A a;
 // private void setA(A a) { this.a = a; }

 public void onEvent(char  e) { if(e == 'b') count++; }
 public int getCount() { return count; }
 public int getAPlusBCount() {
   // We know B count, but we don't know A so how would we change this 
   // so B is A aware? Or not even aware, just somehow coupled? This
   // is the question
   // return a.getCount() + count;
 }

 public void doConditionalHere() {
  // Do some condition in B that relies on the state of data in A
  int acount = 0; // a.getCount(); ???
  if(acount % 2 == 0) {
   this.count--;
  }
 }
}

class Run {
 A a = new A();
 B b = new B();
 List listeners = new List();
 listeners.add(a);
 listeners.add(b);

 // The ugly way I add coupling right now is to keep a reference to A
 // inside B. It's commented out because I am hoping there is a more intelligent approach
 // b.setA(a);

 for(char c : "ababbabab") {
   for(listener : listeners) {
     listener.onEvent(c);
   }
 }
}

person Community    schedule 09.11.2008    source источник
comment
Я собираюсь пойти дальше и принять ваш ответ, так как он работает, но я надеялся на какой-то другой путь. Это глупо. Зачем это принимать? Почему бы не дождаться лучшего ответа?   -  person S.Lott    schedule 10.11.2008


Ответы (3)


Почему бы не иметь центральный объект, который будет отслеживать, сколько раз был запущен метод onEvent для всех классов слушателей?

 public interface CountObserver {

 public void updateCount(String className);
 public int getCount(String className);
}

public class CentralObserver implements CountObserver {

 private int aCount;
 private int bCount;

 public void updateCount(String className) {

 //There's probably a better way to do this than using
 //all these if-elses, but you'll get the idea.

  if (className.equals("AclassName")) {
   aCount++;
  }
  else if (className.equals("BclassName")) {
   bCount++;
  }
 }

 public int getCount(String className) {

  if (className.equals("AclassName")) {
   return aCount;
  }
  else if (className.equals("BclassName")) {
   return bCount;
  }
}

class A implements Listener {

 CountObserver countObserver;

 public void registerObserver (CountObserver countObserver) {

  this.countObserver = countObserver;
 }

 public void onEvent(char e) {

  if(e == 'a') {

   countObserver.updateCount (this.getClass.getName);
  }
 }

}

//Same thing for B or any other class implementing Listener. Your Listener interface should, of 

//course, have a method signature for the registerObserver method which all the listener classes 

//will implement.

class Run {

 private A a;
 private B b; 
 private CountObserver centralObserver;

 public runProgram () {

  centralObserver = new CentralObserver();
  a.registerObserver(centralObserver);
  b.registerObserver(centralObserver);

 //run OnEvent method for A a couple of times, then for B

 }

 public int getAcount () {

 return centralObserver.getCount(a.getClass.getName());
 }

 public int getBcount () {

 return centralObserver.getCount(b.getClass.getName());
 }
} 
 //To get the sum of all the counts just call getAcount + getBcount. Of course, you can always  add more listeners and more getXCount methods
person Sandman    schedule 10.11.2008
comment
Спасибо. Это хорошая идея, но она немного отличается от того, что я ищу. Я рефакторинг примера, чтобы показать его лучше. - person Josh; 11.11.2008

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

Вы можете автоматизировать управление зависимостями, заставив слушателей определять, от кого они зависят. Список слушателей будет упорядочен не по порядку вставки, а для обеспечения того, чтобы зависимые объекты следовали своей зависимости. Ваш интерфейс слушателя будет выглядеть примерно так:

interface Listener {
  String getId();
  Collection<String> getDependencies();
  onEvent(char e);
}

Или просто иметь ссылки, например:

interface Listener {
  Collection<Listener> getDependencies();
  onEvent(char e);
}
person sblundy    schedule 09.11.2008
comment
Не могли бы вы уточнить, что вы подразумеваете под зависимостью от обратного канала? - person Josh; 09.11.2008
comment
Итак, вы говорите, что в приведенном выше псевдокоде первый слушатель должен иметь своих собственных слушателей и помещать второго слушателя в качестве слушателя в первый, а не в список корневых слушателей? - person Josh; 09.11.2008
comment
Да, это может сработать. Для вашего примера мое редактирование было бы лучше. - person sblundy; 09.11.2008
comment
Хм. Это не совсем тот ответ, который я искал, поскольку я не хочу повторно запускать события через иерархию зависимостей. Я собираюсь пойти дальше и принять ваш ответ, так как он работает, но я надеялся на какой-то другой путь. На самом деле мне нужна переменная count (что она представляет). - person Josh; 09.11.2008

«Как бы мы изменили это, чтобы слушатель B был осведомлен о слушателе A? Или даже не осознавал, просто как-то связан?»

Вы не часто хотите соединить два «равноправных» объекта таким образом. Вы хотите, чтобы два одноранговых узла зависели от чего-то общего.

Более глубокий вопрос заключается в том, что слушатель A или слушатель B делают со всей информацией, которую они собирают?

Прослушиватель часто делает две вещи: собирает данные и выполняет действия. Часто эти две вещи нужно разделять. Слушатели должны слушать и собирать и делать немного больше. Некоторые другие объекты могут быть активированы прослушивателем.

У вас может быть только один слушатель, который имеет несколько действий (A и B). Затем прослушиватель может предоставить соответствующие подсчеты для A, а также для B. Он предоставляет счет «a» для A. Он предоставляет счет «a» или «b» для B.

person S.Lott    schedule 10.11.2008
comment
В реальной программе слушатель A отслеживает набор Set‹Item›. Listener B совершенно не связан с A в том, что он делает, но часть его логики зависит от того, установлен ли set.contains(item) у lister A. Поэтому я хотел, чтобы слушатели A и B были разделены, поскольку они очень разные по функциям. - person Josh; 11.11.2008
comment
Но они не отличаются. В и А зависят от чего-то общего. B (обычно) не зависит от A. Как правило, они зависят от чего-то общего. - person S.Lott; 11.11.2008
comment
Хм. Кажется, я пытаюсь понять то, чего не понимаю. Заставляет меня задаться вопросом, подходит ли шаблон подписчика. Спасибо за вашу помощь! Не помешал бы и очень короткий псевдокод, может быть, я смогу понять его яснее. - person Josh; 11.11.2008
comment
Абонент в порядке. Поскольку B зависит от A, а также от того, что слушает B, вы можете создать слишком запутанную сеть зависимостей. Не бойтесь отделить логику Б и А от того, что они слушают. - person S.Lott; 11.11.2008
comment
Прямо сейчас я инициализирую каждого слушателя ссылкой на один и тот же объект, представляющий разделяемую часть данных. Кажется, он работает достаточно хорошо. Еще раз спасибо. - person Josh; 11.11.2008