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

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

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

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

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

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

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

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);
   }
 }
}

«Я собираюсь принять ваш ответ, поскольку он работает, но я надеялся на какой-то другой способ». Это глупо. Зачем это принимать? Почему бы не дождаться лучшего ответа?

S.Lott 10.11.2008 05:53
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
1 023
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

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

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

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

Не могли бы вы пояснить, что вы подразумеваете под зависимостью "обратного канала"?

Josh 09.11.2008 04:32

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

Josh 09.11.2008 04:54

Да, это могло сработать. Для вашего примера мое редактирование было бы лучше.

sblundy 09.11.2008 04:59

Хм. Это не совсем тот ответ, который я искал, поскольку я не хочу «обновлять» события через иерархию зависимостей. Я собираюсь принять ваш ответ, поскольку он работает, но я надеялся на какой-то другой способ. На самом деле мне нужна переменная "count" (то, что она представляет).

Josh 09.11.2008 05:05

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

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

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

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

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

В реальной программе слушатель A отслеживает набор Set <Item>. Слушатель B совершенно не связан с A в том, что он делает, но часть его логики зависит от того, содержит ли список A set.contains (item). Поэтому я хотел разделить слушателей A и B, поскольку они очень разные по функциям.

Josh 11.11.2008 00:53

Но они не другие. B и A зависят от чего-то общего. B (как правило) не зависит от A. Обычно они зависят от чего-то общего.

S.Lott 11.11.2008 00:57

Хм. Думаю, я пытаюсь взять на себя то, чего не понимаю. Это заставляет меня задуматься, подходит ли вообще шаблон подписчика. Спасибо за вашу помощь! Также поможет какой-нибудь действительно короткий псевдокод, может быть, я смогу понять его более четко.

Josh 11.11.2008 01:07

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

S.Lott 11.11.2008 02:59

Прямо сейчас я инициализирую каждого слушателя ссылкой на тот же объект, представляющий часть общих данных. Вроде работает "достаточно хорошо". Еще раз спасибо.

Josh 11.11.2008 20:34
Ответ принят как подходящий

Почему бы не иметь центральный объект, который будет отслеживать, сколько раз метод 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

Спасибо. Это хорошая идея, но она немного отличается от того, что я ищу. Я проведу рефакторинг примера, чтобы показать его лучше.

Josh 11.11.2008 00:48

Другие вопросы по теме