woensdag 24 november 2010

NullController pattern - how to evolve a complex RCP Eclipse client application

Introduction

Many tutorials and quite a number of books describe how to get started in Eclipse plug-in development. It is only when trying to apply all of this new found knowledge to real world business applications that things get complicated.

Gradual increase of complexity

The main problem during development of large systems is that all parts evolve in parallel: that database schema, the business logic on the servers, the EJB connection code and the RCP client. So it's time to think about what John Gall, much quoted author on System Theory, wrote:
“A complex system that works is invariably found to have evolved from a simple system that worked. The inverse proposition also appears to be true: A complex system designed from scratch never works and cannot be made to work. You have to start over, beginning with a working simple system.”
That is the inspiration for this blog post: you have to start simple and get it to work, then evolve from there. In this blog entry I will share how with a little forethought you can make you RCP client application easier to develop and test. With RCP client application I mean a front end for a large server based business application where multiple users access real time data using an Eclipse RCP based client. All business logic takes place on the server, the client provides a view to the current data and allows users to modify that data.

Basics: Split between UI and Core plug-ins

A good practice is to divide up your functionality in two plug-ins: one to handle most logic, called the Core plug-in, and another containing all code that requires a UI, called the UI plug-in. Only the UI plugin has a dependency on the eclipse GUI framworks: org.eclipse.swt, org.eclipse.jface, org.eclipse.ui and org.eclipse.ui.workbench.
You can check this using the PDE dependency view.

NullController Pattern

The split between UI and Core is also called the Model View Controller or MVC pattern. Basically the UI subscribes to data updates and translates UI events that modify data into method calls to the controller. In Java terms: the controller in Core has subscribe() and unsubscribe() methods that take an application specific listener interface as parameter.
The trick is to make the location of the controller indirect via a Factory, so it can evolve in complexity over time.

AbstractController

The AbstractController includes the code to handle subscribe() and unsubscribe() of one or more views. It probably also contains the code to broadcast new or changed data to the views.

NullController

The NullController implementation is the simplest possible form of the interface. All action method calls do nothing and all query methods return either null or, when collections, empty instances (so all iterators in views work).
With this controller you can work on the UI view and button layout and do simple developer testing for button and menu enablement.

StubController

The StubController implementation is one step up in complexity. It is entirely resident in the client. All action method calls just provide expected feedback and all query methods return a simple local generated date object or collection. With this controller you can refine the UI interaction by allowing commands based on selection contents and implement selection service code. In this stage you can also demonstrate the UI to customers and users and start the development of training as the perceived behavior of the client will not change much.

LocalTestController

The LocalTestController implementation links the UI data objects directly to local server code, but without the intermediate EJB layer. Here we can test whether the data in the data objects is filled consistently from the underlying databases.

J2EEController

When the above all works correctly it is time to insert the EJB layer. Create the J2EEController and deploy the EAR on a test server and then do the final testing using the RCP client.

Next time

I will fill out these basic steps with some code examples to show in more detail how to implement this.

UPDATE

Sample code for this post is available at Github.