Creating a DSL for Presenter First

Presenter First (P1) is a user interface pattern that extends the Model-View-Presenter (MVP) pattern by disconnecting communication between the model and view. The presenter acts as a mediator between the model and view, which turns the presenter into a declarative statement of features being implemented by the user interface. A domain specific language (DSL) can be defined for the presenter, making the presenter even more declarative and reducing the amount of code developers have to type in order to implement the pattern. The first part of the article makes the case for using a DSL with Presenter First. The second part demonstrates one possible implementation.

Presenter First was introduced in a white paper published by Atomic Object. You can find the white paper and more information about Presenter First here: http://www.atomicobject.com.

The Case for Presenter First

Presenter First is a variant of the Model-View-Presenter (MVP) pattern. A static structure diagram of MVP may look like the figure below. (In practice, the view and model will likely be represented by interfaces.)
MVP.jpg

According to the MVP pattern, the view should only contain display code. The model should contain logic related to the UI element. The presenter is responsible for instantiating the model and view, and handing the view an instance of the model. (The model instance will be handed to the view as an interface.)

Both the presenter and model can be thoroughly unit tested. The view is generally does not have unit tests because presentation code is difficult to test, which is one of the arguments for minimizing hand-written code in the view.

The MVP pattern also allows different views to be used with the same model, assuming that the view and model both implement interfaces.

The major difference between MVP and P1 is that in P1 the view and model do not communicate directly. They use the presenter as a mediator or broker.
P1.jpg

If the user triggers an action that requires logic to be executed in the model (like a button click), the view raises an event that is handled by a delegate in the presenter. The presenter then calls the appropriate method on the model, passing any required event-related data as parameters. The model may also raise events which are handled by the presenter, which call a methods on the view.

For example, if we have a form containing an "Accept Changes" button, when the user clicks the button, the view raises an AcceptChanges event which is handled by a delegate in the presenter called View_AcceptChanges. The presenter's delegate calls a method on the model called PersistChanges. The model's method executes logic related to persisting changes.

The most significant advantage the Presenter First pattern has over Model View Presenter is that the major features of the user interface are defined in a declarative way within the presenter. You can look at the event delegates in the presenter and get a very good idea of what features are being implemented. Here is an example of a delegate in the presenter which handles an event in the view:

void View_AcceptChanges(object sender, EventArgs e)
{
	_model.PersistChanges();
}


P1 suggests an order of operations in which the developer writes the presenter first, which has the side effect of defining the model and view interfaces while writing the presenter. An experienced developer or architect can create an initial version of the presenter as a way of specifying the features or user stories. Other developers can implement and refine the P1 triad during construction.

The primary tradeoff inflicted by Presenter First is that it requires much more code. Atomic Object makes the case that typing can be reduced by using various types of automation, such as static code generation and template expansion in the source code editor. While automation tools may reduce physical typing, the developer still has to understand and manage a large amount of code. Atomic Object states, and I think they are right, that the extra typing is well worth the effort and that developers find the pattern simple and useful.

Creating a Domain Specific Language

The consistency of the P1 pattern makes it a good candidate for creating a small domain specific language(DSL). A DSL can increase the velocity of construction by reducing the amount of typing and unit testing, but its primary benefit is that it raises the level of abstraction by making code more declarative. The presenter is already fairly declarative, so putting a DSL on top of it makes developer intent even more clear. Some alterations have to be made to the Atomic Object version of the pattern (defined later in this article), but I believe the changes are an improvement. Imagine a language that defines the presenter like this:

Presenter
{
	//When the user accepts changes, persist the data
	Feature{View:AcceptChanges causes Model:PersistChanges}

	//The system can show a message to the user
	Feature{Model:ShowMessage causes View:ShowMessage
		Parameters{string message}
	}
}


The user interface defined in the presenter above has two features: the user can accept changes and the model can show a message to the user.

We can infer, and code generate, the entire Presenter First structure below.
P1GeneratedStaticStructure.jpg

Just based on the simple DSL above, we can generate the entire presenter class, interfaces for the model and view, and abstract implementations of the model and view. The abstract model and view contain the events, methods for raising the events, and abstract methods to define the code that must be typed. The only human-typed code is in Model.PersistChanges() and View.ShowMessage(), plus the related test class. All the plumbing code has been generated.

The code created by the DSL has two huge advantages over a hand coded implementation: the developer has to type much less code, which results in greater consistency and fewer defects; and the DSL gives us a more declarative and concise language for expressing intent.

Extending the Pattern and the DSL

When the model needs to update data that is bound to the view, the data could be sent via an event raised by the model, just like the message string in the ShowMessage() example above. However, manipulating data is such a common occurrence that we could consider adding the abstraction of shared properties that are synchronized between the model and view. Underneath, we can use the same eventing mechanism for passing the data. In the example below, we have declared a shared property called AcceptChangesEnabled. The DSL could look like this:
Presenter
{
	[Features...]
	
	Property{Model:AcceptChangesEnabled, View:AcceptChangesEnabled,
	         Type=Boolean}
}


Using a property change notification mechanism, like INotifyPropertyChanged in .NET, the presenter can keep the values (or object references) synchronized between the model and view. If the model updates a property, the view receives the change, and vice versa. Therefore, the view can bind itself to its own properties and the model code can update its properties without worrying about how the view will be updated.

From the perspective of the model, we can write code that sets the "AcceptChangesEnabled" concept without thinking about the user interface elements involved. On the view side, the AcceptChangesEnabled property can be bound to several things like the enabled property on a button, the enabled property of a menu item, and display text in the status bar.

If we were not using a DSL to generate code, creating synchronized properties would be an onerous coding and testing burden.

Comparison with Atomic Object's Presenter First

In Atomic Object's implementation of P1, the presenter plays a coordination role between the model and the view, and some of the application logic is within the presenter as orchestration. In the above example, the presenter is still playing a coordination role, but the scope of what the presenter can do is more limited because it can only have a one-to-one relationship between event and behavior. In other words, there is no orchestration. Some of the need for orchestration has been replaced by adding synchronized properties because the model can update its properties without having to raise an event that would trigger an update on the view. (Actually, an event is raised in the generated code, but the developer does not have to write code for it.) We could extend the DSL to add orchestration. It would add some complexity to the DSL, but it would allow us to declare more of the application's behavior in the DSL instead of having to type it in the model.

Implementing the DSL

Workflow

If orchestration is beneficial in the presenter, why not create the presenter using Windows Workflow Foundation? In fact, WF is a DSL designed for orchestrated processes and state machines. Some work would have to be done to generate the interfaces and abstract classes for the model and view from the workflow.

Xsd, Xml, and CodeSmith

In my office we have defined the Presenter First DSL using an Xml schema. Developers type Xml that conforms to the schema. Code generation templates are written using a product called CodeSmith. The CodeSmith template is invoked by setting the Custom Tool property in the Xml document properties inside Visual Studio. The template deserializes the Xml into an object hierarchy that is used in the template logic. Xsd to Xml to CodeSmith is a common implementation pattern for DSLs at my company. It is very effective because it is simple to create an Xml language in Xsd that can be understood by a CodeSmith template.

The Xml DSL looks like this:
<Presenter>
	<Feature Source="View" Event="AcceptChanges" Target="Model" Action="PersistData"/>

	<Feature Source="Model" Event="ShowMessage" Target="View" Action="ShowMessage">
		<Parameter Name="message" Type="String"/>
	</Feature>
	
	<PropertyMap ModelProperty="AcceptChangesVisible" ViewProperty="AcceptChangesVisible" Type="Boolean" />
</Presenter>


The disadvantage of the Xsd approach to defining the DSL is that Xml languages can be very verbose, and angle brackets are tedious write and read. However, Visual Studio gives us intellisense in the Xml document, and it is still light years ahead of having to type the presenter code and event logic required for P1 pattern.

DSL Tools

A better approach might be to use DSL tools. The first step is to create a domain model of the language. The DSL designer looks like this:
DSLToolsExample.jpg

The second step is to relate the DSL domain model to designer elements.

The project on this site was developed using DSL Tools. It is one implementation of the Presenter First pattern.

Last edited Jan 8, 2008 at 4:45 PM by Bruk, version 13

Comments

No comments yet.