Martin Fowler has an overview of the Presenter-First pattern (although he refers to Presenter-First as "Passive View").
Here is a Java example of Presenter-First (this is a reworked example taken from Atomic Object):
package com.presenterfirst; import java.awt.event.ActionListener; import org.jmock.Mock; import org.jmock.MockObjectTestCase; import com.presenterfirst.utility.KeepObjectConstraint; public class TestPasswordPresenter extends MockObjectTestCase { private static final String PASSWORD = "password"; private Mock view = mock(IPasswordView.class); private Mock model = mock(IPasswordModel.class); private KeepObjectConstraint passwordSubmittedEvent = new KeepObjectConstraint(); public void setUp() throws Exception { view.expects(once()).method("addPasswordSubmittedListener") .with(passwordSubmittedEvent); new PasswordPresenter((IPasswordView) view.proxy(), (IPasswordModel) model.proxy()); } public void testShouldDisplaySuccessWhenCorrectPasswordSubmitted() { view.expects(once()).method("getSubmittedPassword") .will(returnValue(PASSWORD)); model.expects(once()).method("validatePassword").with(eq(PASSWORD)) .will(returnValue(true)); view.expects(once()).method("displaySuccess"); trigger(passwordSubmittedEvent); } public void testShouldDisplayTryAgainWhenIncorrectPasswordSubmitted() { view.expects(once()).method("getSubmittedPassword") .will(returnValue(PASSWORD)); model.expects(once()).method("validatePassword").with(eq(PASSWORD)) .will(returnValue(false)); view.expects(once()).method("displayTryAgain"); trigger(passwordSubmittedEvent); } private void trigger(KeepObjectConstraint event) { ((ActionListener) event.getEvalObject()).actionPerformed(null); } }
package com.presenterfirst.utility; import org.jmock.core.Constraint; /** * This class is used to evaluate an object through the jmock framework, and * keep reference to that object. * * This class is very handy when a test wants to simulate an event. */ public class KeepObjectConstraint implements Constraint { private Object evalObject; public boolean eval(Object object) { evalObject = object; return true; } public StringBuffer describeTo(StringBuffer stringBuffer) { return stringBuffer; } public Object getEvalObject() { return evalObject; } }
package com.presenterfirst; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; /** * The purpose of this class is to encapsulate the high level user request * that a given password input will be validated. */ public class PasswordPresenter { private IPasswordView view; private IPasswordModel model; public PasswordPresenter(IPasswordView view, IPasswordModel model) { this.view = view; this.model = model; addListeners(); } private void handlePasswordBuisnessRule() { if (model.validatePassword(view.getSubmittedPassword())) { view.displaySuccess(); } else { view.displayTryAgain(); } } private void addListeners() { view.addPasswordSubmittedListener(new ActionListener() { public void actionPerformed(ActionEvent e) { handlePasswordBuisnessRule(); } }); } }
package com.presenterfirst; import java.awt.event.ActionListener; /** * Describes the password view functionality. */ public interface IPasswordView { String getSubmittedPassword(); void displaySuccess(); void displayTryAgain(); void addPasswordSubmittedListener(ActionListener listener); }
package com.presenterfirst; /** * Describes the password checker model functionality. */ public interface IPasswordModel { boolean validatePassword(String submittedPassword); }
In creating the presenter class test-first, the model and view interfaces are defined. Notice in the code above that I provide no source code for the concrete model and view classes. These classes do not need to be defined to get the tests in TestPasswordPresenter.java to pass. Later, the model class can be created test-first. The view class, on the other hand, is kept thin and is not tested with unit tests.
By restricting the awareness of the UI library to the concrete view class, the rest of the code is easily testable via unit tests.
Presenter-First is a specific case of a more general pattern, Humble Objects. There are many types of code that hinder testing. For example, code that is aware of threads, transactions, an application container, persistence, IO, time, random numbers, or UI. If awareness of these things is isolated to designated areas, then the rest of the code should be easily testable.