1

MVP pattern assumes that View expose methods to Presenter. For example in View we can write:

public HasClickhandlers getButton() {
    return myBtn;
} 

and access to this method from presenter, like

view.getButton().addclickhanlder() ...

But when I build my app in this style, I have a lot unnecessary code. For example I want to create TablesView and TablesPresenter (I decide thatTablesPresenter and TablesView is minimal entity (minimal module), that can not be divided into more small presenters and views, and can not be more complex). Then, when I create TablesView, I want to put inside this view another custom component - MyCustomTable. And Inside MyCustomTable put MyCustomHeader, inside MyCustomHeader put MyCustomFilter and so on (this sequence may be much longer)... So problem is when I want to access from Presenter to entered text(by user) inside MyCustomFilter, I need to expose method on MyCustomFilter:

//inside MyCustomFilter
public String getFilterText() {
   return textBox.getText();
}

then in widget that contains MyCustomFilter - i.e. in MyCustomHeader I need to expose this method:

//inside MyCustomHeader
public String getFilterText() {
  return myCustomFilter.getFilterText();
}

after it, inside MyCustomTable I need to expose this method:

//inside MyCustomTable
public String getFilterText() {
  return myCustomHeader.getFilterText();
}

After it I need to expose getFilterText() method inside TablesView (that contains MyCustomTable) and after all this operations my presenter can access to text inside MyCustomFilter.. And sometime this sequence is more longer. So how to solve this problem? May be I not understand some things about MVP?

WelcomeTo
  • 19,843
  • 53
  • 170
  • 286

2 Answers2

2

There's no reason your view cannot return a view on one of its components, but you have to think in terms of componentization: that'll break encapsulation, your view then exposes its internals.

Also, keep in mind that one of the most important benefit of MVP in GWT is that you can easily mock your views and unit-test your presenters without a sluggish GWTTestCase. There are trade offs to make your code easily testable/mockable.

Thomas Broyer
  • 64,353
  • 7
  • 91
  • 164
  • thanks, so you suggest me to expose method that return entire custom component (as you wrote - entire `view`), for example: `public MyCustomHeader getHeader()` instead of returning public String getFilterText() {return myCustomHeader.getFilterText()} ? – WelcomeTo May 14 '12 at 05:24
  • Well you could expose your ``MyCustomHeader`` but then you create a tight coupling between your ``MyCustomHeader`` UI component and your ``Presenter`` and you probably have to mock the entire ``MyCustomHeader`` component to use JUnit tests. Furthermore if you later decide to have a different implementation of your ``MyCustomHeader`` for let's say Tablets or mobile you will run into problems. Instead you could create an interface for your ``MyCustomHeader`` UI component that exposes all the functions that you want to call from your ``Presenter``. – Ümit May 14 '12 at 07:28
  • @Ümit , thanks. So let summarize. First: inside my view and other non-trivial custom UI components(which contains other custom widgets) I need to expose methods that get access to entire containable component, not only methods that get access to methods of containable components. Second: It's better to prefer using interfaces for all custom components, not realization of them. – WelcomeTo May 14 '12 at 07:42
  • @MyTitle: See my answer. But to summarize. It's always better to reference interfaces instead of realizations/implementations because it makes actually testing of components in isolation possible and also de-couples them (so you can easily switch implementations). Regarding exposing the entire containable component vs exposing some methods: There is no general rule for that and it depends on your use case. I guess I would start with exposing just the functions that are also really accessed by your presenter. – Ümit May 14 '12 at 08:27
  • There's no _it's better to…_ rules; first understand the implications and then pick your rule and stick to it. The whole point of MVP with GWT is to a) abstract your view so you can (theoretically) replace it with another implementation b) make testing faster by allowing you to mock the view and thus avoid using a `GWTTestCase`. There's a trade off between exposing the structure of your view (as components, themselves abstractized behind interfaces) at the expense of having to mock many things, and hiding it at the expense of writing many _trampoline methods_ (as you did). – Thomas Broyer May 14 '12 at 08:36
  • In my opinion, you view should be seen by the presenter as a black-box: what matters is _what_ it does, not _how_ it does it. From the presenter POV, it doesn't matter whether you _submit_ a form from a `KeyUpHandler` on a `TextBox` looking for the _enter key_, or from a _Submit_ `Button`. In your case, I think your view should expose a single `getFilterText`, because it doesn't really matter _where_ the filter component is. – Thomas Broyer May 14 '12 at 08:40
1

One way to solve it would be the use of interfaces. By that you could expose a view of your components without creating a tight coupling between the view and the presenter.

Something along these lines:

public interface CustomFilter {
   String getFilterText();
   // other methods that might be accessed from your presenter
}

public class MyCustomFilter implements CustomFilter {

    @Override
    public String getFilterText() {
        return textBox.getText();
    }
}

You can do the same thing for the other componenents:

CustomHeader:

public interface CustomHeader {
   CustomFilter getFilter();
   // or String getFilterText()
   // other methods that might be accessed from your presenter
}

public class MyCustomHeader implements CustomHeader {

    @Override
    public CustomFilter getFilter() {
        return myCustomFilter;
    }
}

CustomTable:

public interface CustomTable {
   CustomHeader getHeader();
   // or Sring getFilterText();
   // other methods that might be accessed from your presenter
}

public class MyCustomTable implements CustomTable {

    @Override
    public CustomHeader getHeader() {
        return myCustomHeader;
    }
}

You can decide if you only want to expose in each interface just a String or the entire client component's interface. If you expose the entire interface you might violate the law of demeter by having calls like this in your presenter:

getView().getTable().getHeader().getFilter().getFilterText(). 

Probably a better solution would be to define Strings in your interfaces which delegate the calls up the hierarchy to the your CustomFilter interface: getView().getTable().getFilterText(); or even getView().getFilterText().

The other benefits of using interfaces is that you can easily mock them for Unit testing your presenters and even view components in isolation and you can easily create an switch between different implementations of your UI components (for example one that is optimized for smartphones, etc) without changing anything in your Presenters

Ümit
  • 17,379
  • 7
  • 55
  • 74