views:

345

answers:

3

Working with a traditional listener callback model. I have several listeners that collect various stuff. Each listener's collected stuff is inside the listener in internal structures.

The problem is that I want some of the listeners to be aware of some of the "stuff" in the other listeners.

I enforce listener registration order, so if I knowingly register events in some order a later listener can be sure that a previously listener updated its stuff and somehow access it to do more stuff.

My first attempt at this is to have each listener store a reference to the listeners upon which it depends. So I register listeners in the order of those without dependencies to those with prior-registered dependencies, and then set the references between the listeners in various methods.

I am starting to realize how bad this feels and I was wondering if somehow has been down this road before. What would be a more appropriate pattern when one of the listeners needs to access stuff in another?

Here is some pseudocode to illustrate:

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);
   }
 }
}
+2  A: 

You've describing a lot of coupling here. Best would be to eliminate all this back-channel dependency, but failing that maybe you could have those with dependencies listening not on the initial listener list, but on whatever they are dependent on. Or you could have them wait till they have all the signals.

You could automate the dependency management by having the listeners identify who they are dependent upon. The listener list would be ordered not by insertion order, but to insure dependent objects follow their dependency. Your listener interface would look something like this:

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

Or just have the references, like so:

interface Listener {
  Collection<Listener> getDependencies();
  onEvent(char e);
}
sblundy
Could you clarify what you mean by "back-channel" dependency?
Josh
Inter-listener dependency.
sblundy
So you are saying that in the above pseudocode, the first listener should have its own listeners and put the second listener as a listener on the first instead of the root listener list?
Josh
Yeah, that could work. For your example, my edit would be better.
sblundy
Hmm. It's not quite the answer I was looking for, since I don't want to "refire" the events through the dependency hierarchy. I am going to go ahead and accept your answer, since it works, but I was hoping for some other way. Really what I am after is the "count" variable (what it represents).
Josh
+1  A: 

"How would we change this so that Listener B is Listener A aware? Or not even aware, just somehow coupled?"

You don't often want to couple two "peer" objects like this. You want two peers to depend on something common.

The deeper question is what does Listener A or Listener B do with all the information they collect?

A Listener often does two things: it collects data and it takes action. Often these two things need to be separated. Listeners should listen and collect and do little more. Some other object(s) can be activated by a Listener.

What you may have is just one listener which has several actions (A and B). The listener can then provide appropriate counts to A as well as B. It provides an 'a' count to A. It provides an 'a' or 'b' count to B.

S.Lott
In the real program, listener A is keeping track of a Set<Item> set. Listener B is totally unrelated to A in what it does, but part of its logic depends on whether the lister A set.contains(item). So I wanted to keep listener A and B separate since they are so different in function.
Josh
But they aren't different. B and A depend on something in common. B doesn't (generally) depend on A. Generally, they depend on something mutually shared.
S.Lott
Hmm. I guess I am trying to take on something I don't understand. Makes me wonder if the subscriber pattern is even appropriate. Thanks for your help though! Some really short pseudocode would also help, maybe I can understand it more clearly.
Josh
Subscriber's fine. Since B depends on A as well as whatever B is listening to, you might create a too-tangled web of dependencies. Don't be afraid to separate the logic B and A do from the listening that they also do.
S.Lott
Right now I am initializing each listener with a reference to the same object representing the shared data part. It seems to be working "good enough". Thanks again.
Josh
+1  A: 

Why not have a central object that will keep track of how many times the onEvent method was fired for all the listener classes

 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
Sandman
Thanks. This is a good idea, but it is a little different that what I am looking for. I will refactor the example to show it better.
Josh