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

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

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

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

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

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

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

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

Ответы [ 3 ]

2 голосов
/ 09 ноября 2008

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

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

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

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

interface Listener {
  Collection<Listener> getDependencies();
  onEvent(char e);
}
1 голос
/ 11 ноября 2008

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

«Как бы мы изменили это так, чтобы Слушатель В был в курсе, а Слушатель А?

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

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

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

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

...