Showing posts with label design pattern. Show all posts
Showing posts with label design pattern. Show all posts

Thursday, December 6, 2012

Mediator pattern with occamsrazor.js

The mediator design pattern can be very useful to decouple a group of objects. Let's see how.

Let's say we have two very simple objects. The first is an archive of numbers:

var archive = (function (){
    var numbers = [];
    return {
        getNumbers: function (){
            return archive;
        },
        addItem: function (number){
            archive.push(number);
        }
    };
}());

The second object prints the sum of the numbers of the previous object:

var printsum = (function (){
    return {
        print: function (n){
            console.log(n);
        },
        sum_and_print: function (archive){
            var i,sum = 0;
            for(i = 0;i < archive.length; i++){
                sum += archive[i];
            }
            this.print(n)
        }
    };
}());

Ok. Now we need to recalculate and print the sum each time a new item is added to the archive. The first solution is straightforward:

...

        addItem: function (number){
            archive.push(number);
            // we call directly the function
            printsum.sum_and_print(archive);
        }
...

This could be a solution but It has a drawback: we bind explicity the two objects together. With many objects and direct dependencies this could be very difficult to manage. Moreover, chaging or adding other dependencies could translate in changing extensively the (already tested) code.

The mediator

The mediator is an object that mediates between other objects. Instead of calling functions directly we notify events to the mediator. The mediator calls every function that is subscribed to a certain event.
Let's rewrite the example using occamsrazor.js to build a mediator:

//mediator

var pubsub = occamsrazor();

// we need 2 validators, one for the event and the second for the object

// this validate the type of the event

var is_changed_event = occamsrazor.validator(function (evt){
    return evt === 'changed';
});


// this validate the type of the object

var is_archive = occamsrazor.validator(function (obj){
    return 'getNumbers' in obj;
});


// the archive object

var archive = (function (){
    var numbers = [];
    return {
        getNumbers: function (){
            return numbers;
        },
        addItem: function (number){
            numbers.push(number);
            // this notify the event to the mediator
            pubsub.publish('changed', this);
        }
    };
}());

// the printsum isn't changed

var printsum = (function (){
    return {
        print: function (n){
            console.log(n);
        },
        sum_and_print: function (archive){
            var i,sum = 0;
            for(i = 0;i < archive.length; i++){
                sum += archive[i];
            }
            this.print(sum);
        }
    };
}());

// subscribe the event

pubsub.subscribe(function (evt, archive){
    printsum.sum_and_print(archive.getNumbers());
}, [is_changed_event,is_archive])

Build a mediator with occamsrazor.js has another advantage: events are notified only for the objects that pass every validator.

Next challenge: try to integrate occamsrazor.js with backbone.js 
Stay tuned !

Tuesday, October 30, 2012

Enhancing Backbone.js with Occamsrazor.js

WARNING. This article is an early experiment ! This way to enhance backbone.js is much better!

In this post I want to share a little experiment I made with Backbone.js and Occamsrazor.js. But before that I want to explain briefly what Backbone.js and Occamsrazor.js are.

Backbone.js

Backbone.js is a Javascript library useful to build structured UI. It's main role is to coordinate one or more models with their visual representations (views). It also manage the data exchange with the backend. (It is designed to be used in the frontend).
The core of Backbone.js is made of three powerful types of object:
  • models
  • collections
  • views
The model is an atomic unit of data. The collection is a group of objects that share the same model. The view keeps updated the visual representation of a model or a collection. The coordination between each these object is decoupled using events (observer pattern).

The web is plenty of Backbone.js documentation. For example:

Occamsrazor.js

Occamsrazor.js is a plugin system I wrote some time ago. I wrote about that here and here.

Why use these libraries together ?

With Occamsrazor.js a collection can contain objects of different types. To say the truth, in my experiment there is only one type of model but it can behave differently based on its fields.
For example we can decide which view is more appropriate.

The results

The demo is here (it is quite funny, I put in box2d and other libraries).
The "CanvasPlayView" view is responsible to draw all of the bodies on a canvas. In the render method we found this:

        models.bodies.each(function (element, index){
            var pos, x, y, angle;
            pos = positions[element.id];
            if (! pos){
                return;
            }
            x = pos.x;
            y = pos.y;
            angle = pos.angle;

            canvasLayer.ctx.save();
            canvasLayer.ctx.translate(x * canvasLayer.scale, y * canvasLayer.scale);
            canvasLayer.ctx.rotate(angle);

            plugins.drawer(element).render(canvasLayer);

            canvasLayer.ctx.restore();

        });

"Plugin.drawer" is not a regular function. It's a plugin!
This plugin is added in the rect module (the module containing all the plugins for the rectangular bodies) :

plugins.drawer.add(function (element){
    var GenericModelView = Backbone.View.extend({
        render: function(canvasLayer){
            var m = this.model,
                scale = canvasLayer.scale,
                w = m.get('width') * scale,
                h = m.get('height') * scale;

            canvasLayer.ctx.fillStyle = m.get('color');
            canvasLayer.ctx.fillRect(
                -(w / 2),
                -(h / 2),
                w,
                h
            );

            return this;
        }
    });
    return new GenericModelView({model:element });

}, plugins.validators.isBox);

More info on how Occamsrazor.js works are here.

I consider this experiment a step in the right direction.
Soon I'll try to make further steps.