Wednesday, 4 July 2012

Separating Look/Feel from View/Presenter

Here's a demo of a working iPad-style on-off switch that I wrote in SVG and Javascript (GWT).

SVG seems to work pretty consistently across platforms – including the iPad, iPhone and all modern desktop browsers, astonishingly even IE 9. The only unfortunate exception is Android where, apparently, SVG support was actively removed to reduce the browser footprint (by a measly 1MB). You can work around it by installing Android Firefox which supports SVG though not multi-touch.

As I developed the on-off switch control, I separated out two distinct interfaces that I called: View and Look. To give them a proper namespace, without too much clutter, I nested the interfaces inside an outer interface for the OnOffSwitch something like this:

public interface OnOffSwitch {

interface View {

void setOn(boolean on);

boolean isOn();

HandlerRegistration addValueChangeHandler(
ValueChangeHandler<Boolean> handler);
}


interface Look {

void knobDown();

void knobUp();

/**
* @param position
* A value between zero and one (0 = OFF, 1 = ON)
*/

void setKnobPosition(double position);
}
}

The Look is called by a Feel object that translates mouse-clicks and touches. The View is called by the application code. My SVGOnOffSwitch widget implements both Look and View interfaces and hooks itself up to a default Feel object. Testing the Feel object was easy to do by passing it a mock Look.

It's always hard coming up with good names for interfaces and objects. I thought I'd post this because I was pleased with the way the code ended up.

Tuesday, 19 June 2012

A "new" alternative to Spring

Objects are composed of objects. The pen in your hand is composed of a nib, a cartridge, and a plastic sheath. The cartridge contains ink, some kind of valve and so forth. The word "pen" is an abstraction that allows us to communicate at a higher-level and ignore all that detail.

However, when you use a dependency injection (DI) framework, like Spring, to wire objects together, everything appears to be at the same level of abstraction.

Abstraction is the key to dealing with complexity

Without being able to distinguish higher-level abstractions it's easy to become mired in low-level implementation detail. The complexity makes it harder to reason about the application and hard to maintain a clean separation of concerns.

Find clusters that can be abstracted

To work at higher levels of abstraction, we need to encapsulate a graph of lower-level objects inside a simpler façade. The GOOS authors call this "the rule of composite simpler than the sum of its parts" (p.53).

Inner objects are not peers

The encapsulated objects are inside the higher-level object. They are not external collaborators of the object. The inner objects are an implementation detail.

To construct our objects we have two basic choices: either (1) we pass inner objects in through the constructor/setters (à la Spring) or (2) we create the inner objects inside the outer object.

Injecting inner-objects breaks encapsulation

If we pass objects in, we make it hard to distinguish internals from peers and we break encapsulation because we are forced to know how the object is implemented when we construct it.

Use "new" instead

The simple alternative is to use the new operator to create the internal objects and wire them together in the outer object's constructor. If we do this consistently through our application, we don't actually need a DI framework. Objects themselves are quite capable of performing the role that a DI container would perform.

If necessary, mix the two approaches

There are occasions when having separated configuration can be useful - e.g. for supporting plugins. There is nothing stopping us from adopting a hybrid of the two approaches. We can use a DI container for those specific objects that need it.

And vice versa: if we're already using Spring, there's nothing to stop us pulling out clusters of objects from the Spring configuration and assembling them in code, one cluster at a time. It doesn't have to be an all-or-nothing transition. It can be done gently.