Работа с традиционной моделью обратного вызова слушателя. У меня есть несколько слушателей, которые собирают разный материал. Собранный материал каждого слушателя находится внутри слушателя во внутренних структурах.
Проблема в том, что я хочу, чтобы некоторые слушатели знали о некоторых «материалах» других слушателей.
Я применяю порядок регистрации слушателя, поэтому, если я сознательно регистрирую события в каком-то порядке, более поздний слушатель может быть уверен, что предыдущий слушатель обновил свои данные и каким-то образом получил доступ к ним, чтобы делать больше.
Моя первая попытка - сделать так, чтобы каждый слушатель сохранял ссылку на слушателей, от которых он зависит. Поэтому я регистрирую слушателей в порядке тех, у кого нет зависимостей, и тех, у кого зависимости зарегистрированы ранее, а затем устанавливаю ссылки между слушателями различными методами.
Я начинаю понимать, насколько это плохо, и мне было интересно, приходилось ли каким-то образом раньше идти по этому пути. Что было бы более подходящим шаблоном, когда одному из слушателей требуется доступ к материалам другого?
Вот какой-то псевдокод для иллюстрации:
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);
}
}
}




Вы описали здесь много взаимосвязей. Лучше всего было бы устранить всю эту зависимость обратного канала, но в противном случае, возможно, у вас могут быть те, у кого зависимости слушают не начальный список слушателей, а все, от чего они зависят. Или вы можете попросить их подождать, пока они не получат все сигналы.
Вы можете автоматизировать управление зависимостями, указав слушателям, от кого они зависят. Список слушателей будет упорядочен не по порядку вставки, а для обеспечения того, чтобы зависимые объекты следовали своей зависимости. Интерфейс вашего слушателя будет выглядеть примерно так:
interface Listener {
String getId();
Collection<String> getDependencies();
onEvent(char e);
}
Или просто укажите ссылки, например:
interface Listener {
Collection<Listener> getDependencies();
onEvent(char e);
}
Не могли бы вы пояснить, что вы подразумеваете под зависимостью "обратного канала"?
Итак, вы говорите, что в приведенном выше псевдокоде первый слушатель должен иметь своих собственных слушателей и помещать второго слушателя в качестве слушателя в первый вместо корневого списка слушателей?
Да, это могло сработать. Для вашего примера мое редактирование было бы лучше.
Хм. Это не совсем тот ответ, который я искал, поскольку я не хочу «обновлять» события через иерархию зависимостей. Я собираюсь принять ваш ответ, поскольку он работает, но я надеялся на какой-то другой способ. На самом деле мне нужна переменная "count" (то, что она представляет).
«Как бы мы изменили это так, чтобы Слушатель Б знал Слушателя А? Или даже не знал, просто каким-то образом связан?»
Вы не часто хотите объединять два «равных» объекта, как этот. Вы хотите, чтобы два партнера зависели от чего-то общего.
Более глубокий вопрос заключается в том, что слушатель A или слушатель B делают со всей собираемой информацией?
Слушатель часто делает две вещи: собирает данные и предпринимает действия. Часто эти две вещи необходимо разделить. Слушатели должны слушать, собирать и делать еще немного. Некоторые другие объекты могут быть активированы Слушателем.
У вас может быть только один слушатель, который имеет несколько действий (A и B). Затем слушатель может предоставить соответствующие подсчеты как для A, так и для B. Он предоставляет счет «a» для A. Он предоставляет счет «a» или «b» для B.
В реальной программе слушатель A отслеживает набор Set <Item>. Слушатель B совершенно не связан с A в том, что он делает, но часть его логики зависит от того, содержит ли список A set.contains (item). Поэтому я хотел разделить слушателей A и B, поскольку они очень разные по функциям.
Но они не другие. B и A зависят от чего-то общего. B (как правило) не зависит от A. Обычно они зависят от чего-то общего.
Хм. Думаю, я пытаюсь взять на себя то, чего не понимаю. Это заставляет меня задуматься, подходит ли вообще шаблон подписчика. Спасибо за вашу помощь! Также поможет какой-нибудь действительно короткий псевдокод, может быть, я смогу понять его более четко.
Абонент в порядке. Поскольку B зависит от A, а также от всего, что B слушает, вы можете создать слишком запутанную сеть зависимостей. Не бойтесь отделить логику Б и А от слушания, которое они тоже делают.
Прямо сейчас я инициализирую каждого слушателя ссылкой на тот же объект, представляющий часть общих данных. Вроде работает "достаточно хорошо". Еще раз спасибо.
Почему бы не иметь центральный объект, который будет отслеживать, сколько раз метод 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
Спасибо. Это хорошая идея, но она немного отличается от того, что я ищу. Я проведу рефакторинг примера, чтобы показать его лучше.
«Я собираюсь принять ваш ответ, поскольку он работает, но я надеялся на какой-то другой способ». Это глупо. Зачем это принимать? Почему бы не дождаться лучшего ответа?