Tuesday, May 13, 2008

MVP in JavaScript

I started with some OO-like concepts in JavaScript with my previous 2-part post. I would like to continue using the mentioned concepts in building something I think is a good way to make applications - MVP.

MVP = Model View Presenter


If you are interested in MVP, take a look here. As you can see, it was "retired" by Fowler. I sill like to call it like this, but be aware that I will be talking about what he calls Passive View. In other words - be aware that there are many ways to implement MVP, depending on your needs. Another similar way is using MVC. There are differences between them, but I like to look at them as a set of options which are very similar, but have enough differences that you can use one over another depending on your needs.

MVP implemented as Passive View is the one I like for several reasons. First, it allows very big degree of separation. Presenter sits in between and there is nothing in View that will ever touch Model. View doesn't depend on the Model. For efficiency reasons, you might transfer Model classes, but generally Presenter handles all transfers. As common to all MXX architectures, Model is already separated by Observer pattern from other two, in this case from the Presenter. Presenter contains all the business logic, whatever that may be. Model can contain only domain logic (e.g. data manipulation methods, which are most of the time rather simple). Presenter never contains any GUI-related code. It acts on the events from the View and sends updates, but doesn't inherently think about the structure of the View. This allows for View switching - you can have multiple views or even Views that are built in different frameworks - for example, Web (HTML) and WebService (SOAP) views.

Ingredients


As you have seen, MVP = Model + View + Presenter. I will try to explain how MVP works on a simple example. Do you (or did you) ever play Sudoku? If you did, you will not have a problem following this article. If you didn't, take a look at the link on Wikipedia. In essence, it is quite simple. You have a 9x9 (in standard Sudoku, which I will stick to) square matrix. It contains 9 rows, 9 columns and 9 blocks (block is a 3x3 sub-square). Here is a picture, taken from Wikipedia:



The goal is also very simple. Think of each of the 9 rows, 9 columns and 9 blocks as a zone. Thus, you have 27 zones. Each of the zones must contain numbers 1-9. Since each zone has exactly 9 little squares, you can also deduce from this that there are no duplicate numbers - each of the numbers 1-9 must appear once and only once.

Now, while the above definition of the objective is quite simple, solving it can be very hard in some cases. Another article on Wikipedia lists two of them at the bottom, named "Top 1465 Number 77" and "Qassim Hamza", in the part called "Exceptionally difficult Sudokus". They are... Or are they? Well, they are if you are a human. Don't, however, forget that there are computers - they can solve the above in seconds, even in JavaScript, which, according to The Computer Language Benchmarks Game is not quite the fastest programming language humans invented...

To explain MVP, I will try to make a structure that will allow you to play, help you with solving and solve automatically the Sudoku puzzles. I chose this one because:
  • It is a simple game that everyone can learn
  • Apparently there are a lot of people playing it or at least being interested in it - this is what Google contrives as the possible number of matches:



    Quite a lot, wouldn't you say?
  • It is not that hard to implement a computer solver that is moderately fast even in JavaScript
  • I installed M-SudoKu on my phone. Quite nice implementation, lot of options (even for cheating, which I never used - what's the challenge if you use it). Darn, it generates very hard puzzles on the most difficult level. I tend not to use any paper for solving them and it can take me hours to solve just one. It's a war!
OK, so this is somewhat how I have been thinking. The first thing - I started from is Model. What's a model? A model is generally the set of data classes - the thing you will be transforming in different ways to satisfy your business (or, in this case, gaming) requirements. It can be a lot of things - you can imagine applications requiring objects like Person, Address, Mail, WeatherCondition, Car, InsurancePolicy, Stock, TrainStation, AirlineTicket and such. What do we need in Sudoku? I thought I needed only three:
  • Cell, representing the small square on the board
  • Board, representing the whole board (which equals to 9x9 Cells)
  • Messages, which is a set of messages that will be displayed to the user
Thus, we have these three classes as model classes, which I put in the folder named model. These classes are all extended from Model (abstract) class. I'll explain it right away.

Model


Each model is the subject in the Observer pattern. In MVP, Presenters are the observers. They subscribe to the underlying model - each Presenter usually have one dedicated or shared model they look after. There is, however, nothing that dictates this - you can have one presenter overlook ten models if you want. There can be a presenter that doesn't have a model it subscribes to. A good example are delegating presenters, which overlook other (sub-)presenters. Look at your top level Presenter. It's the application presenter. In some cases it only groups separate parts of your application - other presenters - and delegate actions between them, connects them. Usually, however, even these presenters have a (small) model.

In the example I am giving, all presenters overlook one model, but some share the same model - Board, in this case. Generally, I think it is wise to have one model per presenter. If you need more things to overlook, I choose to make the artificial layer (facade) between them. This way you can separate the part of the model world into two parts:
  • Top level parts are used directly by the presenters.
  • Their subparts (and other levels more deep) are used by both presenters and model parts above.
In my example, Board is the top level model. It is directly manipulated by the user. Cell is the subpart. While you do show cells, you generally don't show one Cell only. Thus, they are second-level objects compared to Board.

If I ever had the need to overlook two model parts in one presenter - say Board and Timer, a fictional class that would represent time spent on solving the current puzzle - I would make another class (maybe BoardTimer), combine Board and Timer into it and subscribe to notifications from that class, which will in turn subscribe to Board and Timer and just resend the messages up. This is not a good practice (boilerplate code), but it somewhat simplifies things and is not very common not to have meaningful top-level objects, but to have to "invent the hot water" like this.

Now, here is the abstract Model class. It's quite simple, here:
function Model() {
this.setObservers([]);
this.setChanging({});
}
Klass.create(Model);

Model.prototype.notifyAll = function() {
if(this.properties == null)
return;

for(var i = 0; i < this.properties.length; i++) {
this.notify(this.properties[i]);
}
}

Model.prototype.addObserver = function(observer) {
this.getObservers().push(observer);
}

Model.prototype.notify = function(propName) {
if(this.getChanging()[propName])
return;

this.getChanging()[propName] = true;
for(var i = 0; i < this.getObservers().length; i++) {
var observer = this.getObservers()[i];
var method = observer[propName + 'Changed'];
if(method != null)
method.call(observer, this[propName]);
}
delete this.getChanging()[propName];
}

Model.prototype.removeObserver = function(observer) {
for(var i = 0; i < this.getObservers().length; i++) {
if(this.getObservers()[i] == observer) {
this.getObservers().splice(i, 1);
break;
}
}
}

Model.addProperty = function(klass, propName, options) {
var propNameUpper = propName[0].toUpperCase() + propName.substr(1);
var getter = 'get' + propNameUpper;
var setter = 'set' + propNameUpper;
if(options) {
if(options.liftGet)
getter = '_' + getter;
if(options.liftSet)
setter = '_' + setter;
}
prototype = klass.prototype;
prototype[getter] = function() {
return this[propName];
}
prototype[setter] = function(newValue) {
if(this[propName] != newValue) {
this[propName] = newValue;
this.notify(propName);
}
}
if(prototype.properties == null)
prototype.properties = [];
prototype.properties.push(propName);
}

Property.addProperty(Model, 'observers');
Property.addProperty(Model, 'changing');

There are several things to take a look here. Start from the end - there are two properties in this class. Property is the utility singleton that looks like this:
Property = {
addProperty : function(klass, propName, options) {
var propNameUpper = propName[0].toUpperCase() + propName.substr(1);
var getter = 'get' + propNameUpper;
var setter = 'set' + propNameUpper;
if(options) {
if(options.liftGet)
getter = '_' + getter;
if(options.liftSet)
setter = '_' + setter;
}
prototype = klass.prototype;
prototype[getter] = function() {
return this[propName];
}
prototype[setter] = function(newValue) {
if(this[propName] != newValue)
this[propName] = newValue;
}
}
}

Very simple - when you say:
Property.addProperty(Model, 'observers');

it will add a property to the class Model. What is a property? If you come from Java, you could call this getters and setters. After the previous line, all instances of Model will have getObservers and setObservers methods. You could (and should) use these to change the instance. This allows the class to change get/set operations to implements some more logic then the simple get/set. For example, one useful thing is that you can simply override the methods to provide logging, like this:
Property.addProperty(Model, 'observers', { liftGet: true });

Model.prototype.getObservers = function() {
log.debug('getObservers called');
return this._getObservers();
}

Note the liftGet in the property definition - it tells the Property utility singleton to make getter with the underscore. This way, you still have the intended getter for your convenience, so you can call it when necessary. The same thing is liftSet, only this applies to the setter method. In overridden setters you can put e.g. some validation logic:
Property.addProperty(Model, 'observers', { liftSet: true });

Model.prototype.setObservers = function(newObservers) {
if(!(newObservers instanceof Array))
throw 'Array must be given to setObservers';
return this._setObservers(newObservers);
}

Going up the Model class, you find even more important thing for using the properties. As you can see, Model defines addProperty method. This is similar to Property.addProperty, except that there is the extra notification code. This code is used to automatically notify observers of this model whenever the value of the property changes.

For that, Model.notify is used. It will first check for loop-notification. This is a very big problem that can occur sometimes. For example, let's say you have two properties, called number and square. Assume that the outside force (i.e. presenter or higher-level model) can set any of the above and the other gets calculated by the simple formulas square = number * number and number = Math.sqrt(square). If you do nothing about this, then setting the number will trigger setting the square, which in turn will trigger setting the number and so on until you start feeling dizzy.

To stop this, Model has the other property called changing. When it starts notifying about the change of one property, it will set the corresponding key in changing map to true. Whenever the loop is about to occur - in other words, the notification about the change of the same property happens - it will just silently ignore to continue notification. Quite logical - why notify twice when you are sure the notification is already in progress?

The other important thing to notify about the notify method is that it uses JavaScript's dynamic nature. Observer made this way will notify its observers of the property changes by simply invoking a method on the observer. For example, if the property named 'number' has changed, it will call the method 'numberChanged' on the observer. However, not all observers want to observer every property. This is where dynamics kick in - if the method is not present, then the given observer (of all observers that are looped over) will just be skipped, as if it has not been the observer. This is in fully what we want - subscribe and get notified only about the things that we care.

There are three more methods. notifyAll is the simplest of all - it just notifies all the observers that all the properties changed. This is very useful for one thing. The creation order in MVP is Model/View then Presenter - because Presenter needs both a model and a view to work properly. However, this means that whatever startup notification happened will simply be lost. To circumvent this, you can easily simulate it - Presenter, in its constructor, calls this method (after it subscribed as the observer to the model) to, effectively, notify itself of the starting state of the model. This is the initial setup procedure.

The other two methods seem fairly self-explanatory - addObserver adds the observer to the model, making it the target of all notifications. Method removeObserver does the opposite - removes the observer, which will stop getting the notifications about model's property changes.

I used MVP in a few projects, liked it quite a bit. It's not for small projects. You can use it - no benefit, though. For larger projects, it is very useful.