Question

I'm trying to learn MVP, but something eludes me; if the Presenter uses the view as an interface, then the View cannot be just a simple rendering of controls. Imagine trying to write a typing-practice game, where words are randomly generated into the UI and the user must type the words as they fall down the screen.

So the view is going to have methods like:

public interface View {
    addWord(String word, double x, double y); // or possibly (Word word)
    moveWord(String word, double distance);
    removeWord(String word);
    setScore(int score);
    registerKeyListener(KeyListener listener);
    // other stuff
}

But ultimately the VIEW is going to have to be responsible for creating custom controls. A lot of code is omitted here, but hopefully this is enough to illustrate what I mean. Example:

public class SwingView {
    private JPanel thePanel;

    private Map<String, WordComponent> currentWords = new HashMap<>();

    public SwingView() {
        thePanel = new JPanel(new WordLayout());
        // other stuff
    }

    public void addWord(String word, double x, double y) {
        WordComponent newWord = new WordComponent(word);
        currentWords.put(word, newWord);
        Point2D.Double point = new Point2D.Double(x, y);
        thePanel.add(newWord, point);
    }

    public void removeWord(String word) {
        WordComponent theWord = currentWords.get(theWord);
        thePanel.remove(theWord);
    }
}

Already the View implementation has logic going on. It's maintaining a Map of its WordComponents. I have two classes of my own here, WordLayout implements LayoutManager2, and WordComponent extends JLabel (or something else, but that would be even MORE code).

In theory, the presenter should know nothing about Swing, so I can unit test with a mock that maybe logs to the console or something. But simply managing the Swing objects is a job into itself. Or, what if I wanted to transform this application into a Tomcat Webpage. Now class ServletView is managing the AJAX calls that move the words around. Which has a dependency on the AJAX framework, which is even more work offloaded to the View.

Summary: Are View implementations supposed to have "logic" that manage their own components?

Followup: The code I wrote above probably won't even be responsive, because the Model and the Presenter aren't working on the Event Dispatch thread (or, they are, which is probably worse). Where does the code that hands off display updates to the Event Dispatch thread go? Or, should the Presenter be on the Event Dispatch thread?

Edit: One idea just occurred to me. Having a platform-specific sub-presenter that is aware of implementation details like whether you're using Swing or something else.

Edit2: Still another question, based on @DuncanJones answer. Imagine I wanted to put logic to make the game resizeable and scale the size of everything based on the new size. Would that logic be in the View, or in the Presenter?

Was it helpful?

Solution

The View component must contain sufficient logic to display the interface to the user. Depending upon the framework used, there can be quite a bit of code in the View. The important thing is to ensure business logic lies in the Presenter.

Regarding your secondary query, all Presenter methods will be invoked on the EDT when the View calls them (in the case of Swing). Unless the action required by the Presenter is trivial, I would immediately kick off a background thread to complete the work. That thread will update the View when completed using SwingUtilities.invokeLater().

In fact, to avoid being tied to Swing, I tend to pass my own EventDispatcher class to each Presenter. This is an interface with the same methods as SwingUtilities. I can then substitute in a different class if necessary.

Side note: this can make unit-testing the Presenter with JUnit difficult, because the Presenter method (and the unit test) will complete before the background thread does. I tend to construct each Presenter with an Executor that is responsible for running the background threads. Then, in a unit test environment, I pass in a special Executor implementation that immediately executes the run() method on the same thread. This ensures the unit tests are single-threaded. Example:

public class SingleThreadExecutor implements Executor {
  @Override
  public void execute(Runnable command) {
    command.run();
  }
}

OTHER TIPS

The point of the different flavors of the MVP pattern is to decouple business logic from view logic. Considering that, it's totally fine for having a view which contains a lot of display logic. There are some caveats there though:

Do not use your widgets as data store. The purpose of widgets is to present stuff or to receive input. The presenter and the model are responsible for holding on to state. If the view is stateful, the presenter should be be aware of it and coordinate it. For example, the window size is the view's concern most of the time. But if it is to be persisted to some user settings file, the presenter has to be involved.

There is no requirement that the view is a single, monolithic class. There just has to be a object implementing the interface that the presenter expects, and someone generating the events. For example, Swing uses components like TableModel where the code for specifying the data presentation can be put in. Furthermore, JavaFX and Android make it possible to cut down a lot on the boilerplate code to set up the view.

Events have to be processed in the right thread of course. You could use an event bus which can be told to do the right thing, or pass an ExecutorService into each view. Also, each method to be called by the presenter must offload work to the framework's preferred thread. Better keep that concern out of the presenter, else it will become difficult to test it. The Right Thing To Do depends on the framework.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top