Android-CleanArchitecture icon indicating copy to clipboard operation
Android-CleanArchitecture copied to clipboard

What about communicate between presenters?

Open ZherebtsovAlexandr opened this issue 8 years ago • 8 comments

I need show progressbar in parent fragment in case if child fragment has needed state (for example count elements in ListView). To solve the problem it is necessary that the presenters were able to communicate. The MainPresenter should know about entities size in the ChildPresenter. Is it correct from the point of view MVP if the presenter will know about another presenter? And how this approach can be implemented in Clean Architecture?

====== Main =======

public interface MainView {
    void showLoading();    
}

public class MainFragment implements MainView {

    ChildFragment childFragment;

    private void init(){
        childFragment = new ChildFragment();
        view.add(childFragment);
    }

    public void showLoading(){  
        // show progressbar
    }

}

public class MainPesenter {
    public void showLoading() {             
        getView().showLoading(); //show only if ChildPresenter has count > 0
    }
}



====== Child =======

public interface ChildView {
    void showList(List<Entity> entities);
}


public class ChildFragment implements ChildView {

    public void showList(List<Entity> entities) {
        //show list on UI
    }

}

public class ChildPresenter {

    List<Entity> entities;

    public void init() {        
        entities = getEntities();
        getView().showList(entities);
    }

    public int getCount(){
        return entities.size();
    }

}

ZherebtsovAlexandr avatar Jun 03 '16 09:06 ZherebtsovAlexandr

I was wondering the same thing.

aleksamarkoni avatar Nov 05 '16 21:11 aleksamarkoni

So how this approach can be implemented in Clean Architecture? @ZherebtsovAlexandr

Kevin1466 avatar Dec 02 '16 08:12 Kevin1466

Not sure if this is the best way, but you can use LocalBroadcastReceivers

showLoading() will send a broadcast to the ChildFragment, you check the list size, and then issue another broadcast back to MainFragment.

It's a lot of code for a simple thing, but hey, it isolates the logic within the View layer, and both presenters don't necessarily need to know each other. Basically showLoading() becomes requestToShowLoading()

akiwarheit avatar Dec 06 '16 03:12 akiwarheit

I strongly discourage the "presenter knows about presenter" pattern since that increases coupling. A possible solution would be the use of a bus pattern, or as abovementioned, the use of LocalBroadcastReceivers

mradzinski avatar Jan 17 '17 15:01 mradzinski

+1 for bus pattern. Basically what you want is to isolate everything in the View layer. So only your main fragment & child fragment know each other - not necessarily their presenters.

A tightly coupled approach would be for the MainFragment to call a method ChildFragment that requests to show loading.

akiwarheit avatar Jan 31 '17 21:01 akiwarheit

I'd say event bus, or what I'm doing is putting an event bus in my domain layer... the event bus is just an Rx PublishSubject, so I'm not adding any more dependencies, but this allows my domain elements to communicate with each other in a decoupled way. this event bus has no connection outside of my domain layer. This is described a bit in this talk which is excellent by the way.

alexwhb avatar Feb 01 '17 00:02 alexwhb

Personally i disagree the way many people choose event bus to solve this kind of problems

In my opinion if two presenters need to communicate each other then you should merge these two in just one single presenter. Remember that also communication of model objects or states between two use cases is business logic so maybe the scope of the presenter is bigger than you thought initially.

Consider also that if you use collaborators in the right way then you don't need communication between presenters. Data should be provided by collaborators.

If no one of this solution satisfy your case then simply let the parent fragment implements an interface known by the child presenter, something like:

interface ProgressView {
  void showWorkingState();
  void showIdleState();
}

add this new interface as a second view of the child presenter. On the onAttach method of the child fragment create the ChildPresenter and bind the parent fragment as ProgressView by checking for security if the fragments implements the required ProgressView interface .

The child presenter should looks like this:

public class ChildPresenter {
    List<Entity> entities;
    public ChildPresenter(ChildView view, ProgressView progressView) {
       ...
    }

    public void init() {        
        progressView.showWorkingState(); // i don't know where you need to show the loading case, maybe here
        entities = getEntities();
        
        getView().showList(entities);
        progressView.showIdleState(); // the same as above...
    }

    public int getCount(){
        return entities.size();
    }
}

In this way the view will be notified when a child fragment is working/idle and the view will notify then the presenter about it. The presenter will choose what to do.

tommasoresti avatar Feb 11 '17 00:02 tommasoresti

IMHO, another layer of indirection is needed. For example:

public interface MainPresenter {
    void showLoading();
}

public interface ChildPresenter {
    void init();
    int getCount();
}

public class MainChildPresenter implements MainPresenter, ChildPresenter {
	
    private MainPresenter mainPresenter;
    private ChildPresenter childPresenter;

    public MainChildPresenter(MainPresenter mainPresenter, ChildPresenter childPresenter) {
        this.mainPresenter = mainPresenter;
        this.childPresenter = childPresenter;
    }

    public void showLoading() {      
        // show only if ChildPresenter has count > 0
        if (childPresenter.getCount() > 0) {
            mainPresenter.showLoading();
        }     
    }

    public void init() {
        childPresenter.init();       
    }

    public int getCount(){
        return childPresenter.getCount();
    }
}

====== Main =======

public interface MainView {
    void showLoading();    
}

public class MainFragment implements MainView {

    ChildFragment childFragment;
    MainPresenter presenter; // bind to MainChildPresenter

    private void init(){
        childFragment = new ChildFragment();
        view.add(childFragment);
    }

    public void showLoading(){  
        // show progressbar
    }

}

public class MainPesenterImpl implements MainPresenter {
    public void showLoading() {             
        getView().showLoading(); //show only if ChildPresenter has count > 0
    }
}



====== Child =======

public interface ChildView {
    void showList(List<Entity> entities);
}


public class ChildFragment implements ChildView {

    ChildPresenter presenter; // bind to MainChildPresenter

    public void showList(List<Entity> entities) {
        //show list on UI
    }

}

public class ChildPresenterImpl implements ChildPresenter {

    List<Entity> entities;

    public void init() {        
        entities = getEntities();
        getView().showList(entities);
    }

    public int getCount(){
        return entities.size();
    }

}

imsardine avatar Feb 18 '17 16:02 imsardine