0

I'm using a listener pattern where a class A listens for events from various classes B, C, D with the help of a listener interface I

Essentially the structure looks like:


interface I {
    void generalCallback();
}

class A implements I {
    @Override
    public void generalCallback() {
        /// Do some stuff
    }

    void initCommon(Common common) {
        common.setListener(this);
    }
}

class Common {
    I i; 
    void setListener(I i) {
        this.i = i;
    }
}

class B extends Common {
    void doStuff() {
        i.generalCallback();
    }
}

class C extends Common {
    void doSomeOtherStuff() {
        i.generalCallback();
    }
}

class D extends Common {
    void doSomeGeneralStuff() {
        i.generalCallback();
    }
}

Now, for some reasons, I want to inform A about a specific event of D. So is it okay to add one more method to the interface which would now be specific to a particular client(D here) rather than general.
Updated Code


interface I {
    ...
    void callbackFromD();
}

class A implements I {
    ...
    @Override
    public void callbackFromD() { }
}

class D extends Common {
    ...
    void doSpecificStuff() {
        i.callbackFromD();
    }
}

So my questions are :

  1. Is this a good approach to solve this problem?
  2. Or should I create a new interface just for 1 callback from D?
  3. What happens when I have the requirement for specific callbacks from other classes B and C as well?
3

According to you requirements you have to fallow interface segregation principle that states “Clients should not be forced to depend upon interfaces that they do not use.”

Seperate your inteface according to the seperation of concern. You can follow below link to get basic idea

https://dzone.com/articles/solid-principles-interface-segregation-principle

| improve this answer | |
  • Hey, perhaps my question isn't clear or you didn't read it properly. My use case of interface is as a listener callback(connection between 2 classes) where none of my client is implementing anything, they're just calling another class using an interface. So the link you shared as well as your comment doesn't help. – Shivam Pokhriyal Mar 9 at 4:06
1

My simple reply would be that, "yes, it is okay." If D has a particular need to inform A of a thing which only A needs to be concerned with, then simply attach the appropriate logic to A and D and be done.

If you want to express this in terms of an interface, go right ahead and define a brand-new interface. But if you're only going to use this for A and D then it might be confusing to others ... they might be misled into thinking that what you've done is or may soon be "more general." (If it turns out not to be, you can always spin-off that new interface later ...)

We always try to design software initially in the most flexible and generic way, but the time eventually comes where you need to prudently make an exception: one or more of the classes naturally start to diverge. Don't sweat it too much. Don't try to generalize something that isn't [yet...?] general.

Also, you should be maintaining a "running log" of your project where you can discuss your problem and how you solved it, and to make this become a permanent part of the project record that you can refer-to by appropriately placed comments in the source code.

| improve this answer | |
1

I don't think this is a good approach, because then the interface will be tied forever to knowing about D and essentially becomes more of a specialization rather than a general interface.

I think for this you can implement a kind of registry listener, a bit like an observer pattern, where you can register listeners and particular receivers to receive information about particular events, all while being general and reusable and I think in the long run will be even easier to implement and maintain

| improve this answer | |
  • 1
    +1 for because then the interface will be tied forever to knowing about D – Shivam Pokhriyal Mar 16 at 5:56
1

If you modify your common class:

class Common {
    **List<I> i;** 
    void setListener(I i) {
        **this.i.add(i);**
    }
}

you can add another listener for the specific D event. In this way you can add as many event listeners as are needed and all the callbacks can be called.

The advantage to this is that you do not need to change the interface and pollute it with concepts that are most specific than necessary.

The disadvantage with this aproach is that all the callbacks will need to have the same method signature. So if your callback from D needed to return something different or take more arguements then you would need a new interface.

In your specific case I think you might be asking the question because you want A to know that it is D that is sending A the message. If this is the case, and still trying to follow the same approach as above, I would suggest adding the sender as a parameter in the callback method, so D can send itself (this) to A and change the interface to reflect this.

| improve this answer | |
0

I think it depends upon if all classes B,C and D will need different callback OR if B, C, D have some common callbacks and D also has some extra callback.

Following works fine if D's special implementation is the only implementation, then that method can call callBack from AD as AD would be injected into D instead of AG as implemenation of I.

interface I {
    ...
    void callback();
}

class AG implements I {
    ...
    @Override
    public void callback() { }
}

 class AD implements I {
    ...
    @Override
    public void callback() { }
}

class D extends Common {
    //Here I implementation could be of AD instead of AG.
   I ad;
    void doSpecificStuff() {
        ad.callback();
    }
}

But if D has two methods one general and one specific then it would be better to have two instances of I inside D as:

interface I {
    ...
    void callback();
}

class AG implements I {
    ...
    @Override
    public void callback() { }
}

 class AD implements I {
    ...
    @Override
    public void callback() { }
}

class D extends Common {
    I ag;
    I ad;

    void doGeneralStuff() {
       ag.callback();
     }
    void doSpecificStuff() {
        ad.callback();
    }
}

If you add calllBackForD inside interface I the all implementations would have to implement that method even if not required and that is a violation of the Interface Segregation Principle that @Jahangir was talking about.

Update:

Answering specific questions:

Is this a good approach to solve this problem? No, it is not good design to pollute interface I with specific methods.

Or should I create a new interface just for 1 callback from D? The interface can be I itself. But there would be multiple implementations like AG for general callbacks AD for D specific callbacks.

What happens when I have the requirement for specific callbacks from other classes B and C as well? Then you add implementations of I as AB and AC for B and C specific callbacks.

| improve this answer | |
  • I guess my question is not clear at all. Even my comment on @Jahangir answer isn't helping. – Shivam Pokhriyal Mar 13 at 6:38
  • Updated answer with answers to your specific questions at bottom. – Nils Mar 13 at 6:50
0

I would put a single method with multiple implementations. Each implementation would receive an object as a parameter, just like it was a message. Then, depending on the message the underlying logic would be triggered.

interface I {
    ...
    void callback(GenericMessage G);
    void callback(SpecificMessage D);

}

class AG implements I {
    ...
    @Override
    public void callback() { }
}

 class AD implements I {
    ...
    @Override
    public void callback() { }
}

class D extends Common {
    I ag;

    void doGeneralStuff() {
       GenericMessage g;
       ag.callback(g);
    }

    void doSpecificStuff() {
        SpecificMessage d;
        ag.callback(d);
    }
}
``
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.