Страницы

среда, 19 марта 2014 г.

Node.js. Шаблон проектирования декоратор

Пример использования озвученного в заголовке шаблона: декоратор позволяет динамически добавлять объекту функционал для определения его желаемого поведения. На первый взгляд звучит мудрено, но на самом деле все гораздо проще. Приступим, устроим Big Bada Boom :).


К примеру у нас есть конструктор объекта Boom с методом bang, вызов которого выводит в консоль свойство todo созданного объекта:
function Boom(todo) {  
  this.todo = todo || 'Boom';  
}

Boom.prototype.bang = function () {  
  return this.todo + '!';
};

module.exports = Boom;

Импортируем модуль в приложение, создаем объект, вызываем его единственный метод:
var Boom = require('./boom'),
    boom = new Boom();
console.log(boom.bang());


Для начала предлагаю сразу же применить еще один известный шаблон, который называется шаблоном принудительного использования "new", для того, чтобы создавать объект без оператора "new":
var boom = require('./boom')();
console.log(boom.bang());

Это упростит создание объекта, но немного усложнит исходный код самого объекта:
function Boom(todo) {
  if (!(this instanceof Boom)) {
    console.log('Warn: Boom constructor called without "new" operator');
    return new Boom(todo);
  }
  this.todo = todo || 'Boom';  
}

Boom.prototype.bang = function () {  
  return this.todo + '!';
};

module.exports = Boom;


Приступаем к "декорированию", смысл которого заключается в том, чтобы предоставить возможность расширения базового функционала объекта - Boom! - в зависимости от определенных условий.

Добавим в конструктор объекта свойство decorators, в котором будем хранить массив "декораторов", которые необходимо применить к объекту. Сами "декораторы" реализуем как свойства конструктора. В методе bang применяем "декораторы" из массива один за другим:
function Boom(todo) {
  if (!(this instanceof Boom)) {
    console.log('Warn: Boom constructor called without "new" operator');
    return new Boom(todo);
  }
  this.todo = todo || 'Boom';
  this.decorators = [];
}

Boom.prototype.decorate = function(decorator) { 
  this.decorators.push(decorator); 
}; 

Boom.decorators = {}; 
Boom.decorators.big = { 
  bang: function (todo) { 
    return ('Big ' + todo).toUpperCase();
  } 
};

Boom.decorators.bada = { 
  bang: function (todo) { 
    return 'Bada ' + todo;
  } 
};

Boom.prototype.bang = function () {
  var todo = this.todo;
  this.decorators.forEach(function(name) {
    todo = Boom.decorators[name].bang(todo);
  });
  return todo + '!';
};

module.exports = Boom;

Миксуем "декораторы" в различных сочетаниях:
- Bada Boom:
var boom = require('./boom')();
boom.decorate('bada');
console.log(boom.bang());


- Big Boom:
var boom = require('./boom')();
boom.decorate('big');
console.log(boom.bang());


- и, наконец, Big Bada Boom:
var boom = require('./boom')();
boom.decorate('bada');
boom.decorate('big');
console.log(boom.bang());


Эта реализация шаблона позволяет не только добавлять, но и удалять "декораторы".

Вот и все, пятый элемент обнаружен, мир спасен :).