views:

153

answers:

1

I need some simple objects that could become more complex later, with many different properties, so i thought to decorator pattern. I made this looking at Crockford's power constructor and object augmentation:

//add property to object
Object.prototype.addProperty = function(name, func){
    for(propertyName in this){
        if(propertyName == name){
            throw new Error(propertyName + " is already defined");
        }
    }
    this[name] = func;
};

//constructor of base object
var BasicConstructor = function(param){

    var _privateVar = param;
    return{
        getPrivateVar: function(){
            return _privateVar;
        }
    };
};

//a simple decorator, adds one private attribute and one privileged method 
var simpleDecorator = function(obj, param){

    var _privateVar = param;

    var privilegedMethod1 = function(){
        return "privateVar of decorator is: " + _privateVar;
    };

    obj.addProperty("privilegedMethod1", privilegedMethod1);

    return obj;
}

//a more complex decorator, adds public and private properties
var complexDecorator = function(obj, param1, param2){

    //private properties
    var _privateVar = param1;
    var _privateMethod = function(x){
        for(var i=0; i<x; i++){
            _privateVar += x;
        }
        return _privateVar;
    };

    //public properties
    var publicVar = "I'm public";
    obj.addProperty("publicVar", publicVar);

    var privilegedMethod2 = function(){
        return _privateMethod(param2); 
    };
    obj.addProperty("privilegedMethod2", privilegedMethod2);

    var publicMethod = function(){
        var temp = this.privilegedMethod2();
        return "do something: " + temp + " - publicVar is: " + this.publicVar;
    };
    obj.addProperty("publicMethod", publicMethod);

    return obj;
}

//new basic object
var myObj = new BasicConstructor("obj1");

//the basic object will be decorated
var myObj = simpleDecorator(obj, "aParam");

//the basic object will be decorated with other properties
var myObj = complexDecorator(obj, 2, 3);

Is this a good way to have Decorator Pattern in javascript? Are there other better ways to do this?

+4  A: 

There are various implementations of the Decorator pattern in Javascript on Wikipedia and other sites - (1), (2), (3). The pattern is defined as:

the decorator pattern is a design pattern that allows new/additional behaviour to be added to an existing object dynamically.

Object extension is already build into the language itself. Objects can be easily extended, and properties can be added anytime. So why should you have to jump through hoops to achieve this? Shouldn't something like this suffice:

var person = { name: "Jack Bauer" };

// Decorated the object with ability to say a given phrase
person.say = function(phrase) {
    alert(phrase);
}

// Using the decorated functionality
person.say("Damn it!");

If you want a method to apply to all objects that were created using this function, then add that method/properties to the function's prototype.

Update: If you have clearly defined pieces of functionality which can be mixed and matched as needed into certain types of objects, then the MooTools approach of extending and mixing in behavior into objects is nicely done. To give an example, consider a UI component that can be resized, dragged around with a handle, and deleted by clicking a tick mark at the top-right corner. You may not want to create each component with these behaviors but define all these behaviors separately each into their own object. And later mix in these behaviors into each type of component as needed.

var Resizable = {
   ...
};

var Draggable = {
   ...
};

var Deletable = {
   ...
};

var someLabel = new Label("Hello World");

// one way to do it
someLabel.implement([Resizable, Draggable, Deletable]);

// another way to do it
someLabel.implement(Resizable);
someLabel.implement(Draggable);
someLabel.implement(Deletable);

It looks better and more intuitive (to me) than doing something like

var awesomeLabel = new Resizable(new Draggable(new Deletable(someLabel)));

because we are still dealing with a label, and not some resizable, or some draggable, or some deletable object. Another small point, but still worth mentioning is that the parentheses start getting unmanageable after 3 or 4 decorators, especially without good IDE support.

Anurag
Yes, i thought that decorator in javascript doesn't seem to be really helpful in most of cases. But think about objects that manage user interface. If I start with simple objects for basic interfaces, and then i want to add dinamically some defined functionality, like resize or change color or others, but not all together, like using inheritance, just on the object that I want, i think decorator pattern should be useful. It avoids combinatory explosion of object options, and you can add a new complex decorator, when you need it, without have to modify every single object.
Manuel Bitto
I've thought long and hard about this.. TWSS, and come to the conclusion that decorator and mixins are very similar. A mixin extends the functionality of an object, and doesn't necessarily modify only the base object's behavior. And looking at it from that perspective, I really like the way MooTools does it. Updating the answer with more info about that.
Anurag